]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/intltest/tsmthred.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / test / intltest / tsmthred.cpp
CommitLineData
f3c0d7a5
A
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
b75a7d8f 3/********************************************************************
b331163b 4 * COPYRIGHT:
2ca993e8 5 * Copyright (c) 1999-2015, International Business Machines Corporation and
b75a7d8f
A
6 * others. All Rights Reserved.
7 ********************************************************************/
8
729e4ab9 9#include "simplethread.h"
46f4442e 10
374ca955
A
11#include "unicode/utypes.h"
12#include "unicode/ustring.h"
13#include "umutex.h"
14#include "cmemory.h"
15#include "cstring.h"
3d1f044b 16#include "indiancal.h"
374ca955 17#include "uparse.h"
729e4ab9 18#include "unicode/localpointer.h"
374ca955
A
19#include "unicode/resbund.h"
20#include "unicode/udata.h"
21#include "unicode/uloc.h"
22#include "unicode/locid.h"
23#include "putilimp.h"
51004dcb
A
24#include "intltest.h"
25#include "tsmthred.h"
26#include "unicode/ushape.h"
57a6839d 27#include "unicode/translit.h"
b331163b
A
28#include "sharedobject.h"
29#include "unifiedcache.h"
30#include "uassert.h"
4388f060 31
b75a7d8f 32
b75a7d8f
A
33MultithreadTest::MultithreadTest()
34{
35}
36
37MultithreadTest::~MultithreadTest()
38{
39}
40
b75a7d8f
A
41#include <stdio.h>
42#include <string.h>
43#include <ctype.h> // tolower, toupper
0f5d89e8 44#include <memory>
b75a7d8f
A
45
46#include "unicode/putil.h"
47
729e4ab9 48// for mthreadtest
b75a7d8f
A
49#include "unicode/numfmt.h"
50#include "unicode/choicfmt.h"
51#include "unicode/msgfmt.h"
52#include "unicode/locid.h"
51004dcb 53#include "unicode/coll.h"
374ca955 54#include "unicode/calendar.h"
b75a7d8f
A
55#include "ucaconf.h"
56
374ca955 57
b331163b 58void MultithreadTest::runIndexedTest( int32_t index, UBool exec,
b75a7d8f
A
59 const char* &name, char* /*par*/ ) {
60 if (exec)
61 logln("TestSuite MultithreadTest: ");
0f5d89e8
A
62
63 TESTCASE_AUTO_BEGIN;
64 TESTCASE_AUTO(TestThreads);
b75a7d8f 65#if !UCONFIG_NO_FORMATTING
0f5d89e8 66 TESTCASE_AUTO(TestThreadedIntl);
b75a7d8f 67#endif
b75a7d8f 68#if !UCONFIG_NO_COLLATION
0f5d89e8 69 TESTCASE_AUTO(TestCollators);
b75a7d8f 70#endif /* #if !UCONFIG_NO_COLLATION */
0f5d89e8
A
71 TESTCASE_AUTO(TestString);
72 TESTCASE_AUTO(TestArabicShapingThreads);
73 TESTCASE_AUTO(TestAnyTranslit);
0f5d89e8 74 TESTCASE_AUTO(TestUnifiedCache);
2ca993e8 75#if !UCONFIG_NO_TRANSLITERATION
0f5d89e8
A
76 TESTCASE_AUTO(TestBreakTranslit);
77 TESTCASE_AUTO(TestIncDec);
3d1f044b
A
78#if !UCONFIG_NO_FORMATTING
79 TESTCASE_AUTO(Test20104);
80#endif /* #if !UCONFIG_NO_FORMATTING */
0f5d89e8 81#endif /* #if !UCONFIG_NO_TRANSLITERATION */
340931cb 82 TESTCASE_AUTO_END;
b75a7d8f
A
83}
84
85
374ca955
A
86//-----------------------------------------------------------------------------------
87//
88// TestThreads -- see if threads really work at all.
89//
90// Set up N threads pointing at N chars. When they are started, they will
2ca993e8 91// set their chars. At the end we make sure they are all set.
374ca955
A
92//
93//-----------------------------------------------------------------------------------
b75a7d8f
A
94
95class TestThreadsThread : public SimpleThread
96{
97public:
98 TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; }
2ca993e8 99 virtual void run() { Mutex m;
b331163b 100 *fWhatToChange = '*';
b75a7d8f
A
101 }
102private:
103 char *fWhatToChange;
51004dcb 104};
b331163b 105
b75a7d8f
A
106
107void MultithreadTest::TestThreads()
108{
2ca993e8 109 static const int32_t THREADTEST_NRTHREADS = 8;
b75a7d8f
A
110 char threadTestChars[THREADTEST_NRTHREADS + 1];
111 SimpleThread *threads[THREADTEST_NRTHREADS];
73c04bcf 112 int32_t numThreadsStarted = 0;
b75a7d8f
A
113
114 int32_t i;
115 for(i=0;i<THREADTEST_NRTHREADS;i++)
116 {
117 threadTestChars[i] = ' ';
118 threads[i] = new TestThreadsThread(&threadTestChars[i]);
119 }
120 threadTestChars[THREADTEST_NRTHREADS] = '\0';
121
122 logln("->" + UnicodeString(threadTestChars) + "<- Firing off threads.. ");
123 for(i=0;i<THREADTEST_NRTHREADS;i++)
124 {
125 if (threads[i]->start() != 0) {
126 errln("Error starting thread %d", i);
127 }
73c04bcf
A
128 else {
129 numThreadsStarted++;
130 }
b75a7d8f
A
131 logln(" Subthread started.");
132 }
133
3d1f044b 134 assertTrue(WHERE, THREADTEST_NRTHREADS == numThreadsStarted);
b75a7d8f 135
2ca993e8
A
136 logln("Waiting for threads to be set..");
137 for(i=0; i<THREADTEST_NRTHREADS; i++) {
138 threads[i]->join();
139 if (threadTestChars[i] != '*') {
140 errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i);
b75a7d8f 141 }
b75a7d8f
A
142 delete threads[i];
143 }
144}
145
146
2ca993e8
A
147//-----------------------------------------------------------------------------------
148//
149// TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully
150//
151// Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests
152// u_shapeArabic, if the calls are successful it will the set * chars.
153// At the end we make sure all threads managed to run u_shapeArabic successfully.
154// This is a unit test for ticket 9473
155//
156//-----------------------------------------------------------------------------------
157
158class TestArabicShapeThreads : public SimpleThread
51004dcb 159{
2ca993e8 160public:
340931cb
A
161 TestArabicShapeThreads() {}
162 virtual void run() { doTailTest(); }
2ca993e8
A
163private:
164 void doTailTest();
165};
51004dcb 166
b331163b 167
2ca993e8
A
168void TestArabicShapeThreads::doTailTest(void) {
169 static const UChar src[] = { 0x0020, 0x0633, 0 };
170 static const UChar dst_old[] = { 0xFEB1, 0x200B,0 };
171 static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 };
172 UChar dst[3] = { 0x0000, 0x0000,0 };
173 int32_t length;
174 UErrorCode status;
51004dcb 175
2ca993e8
A
176 for (int32_t loopCount = 0; loopCount < 100; loopCount++) {
177 status = U_ZERO_ERROR;
178 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
179 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status);
180 if(U_FAILURE(status)) {
181 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
182 return;
183 } else if(length!=2) {
184 IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
185 return;
186 } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) {
187 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
188 dst[0],dst[1],dst_old[0],dst_old[1]);
189 return;
51004dcb 190 }
51004dcb 191
2ca993e8 192
3d1f044b 193 // Trying new tail
2ca993e8
A
194 status = U_ZERO_ERROR;
195 length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst),
196 U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status);
197 if(U_FAILURE(status)) {
198 IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status));
199 return;
200 } else if(length!=2) {
201 IntlTest::gTest->errln("Fail: len %d expected 3\n", length);
202 return;
203 } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) {
204 IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n",
205 dst[0],dst[1],dst_new[0],dst_new[1]);
206 return;
207 }
51004dcb 208 }
2ca993e8
A
209 return;
210}
340931cb 211
51004dcb 212
2ca993e8
A
213void MultithreadTest::TestArabicShapingThreads()
214{
215 TestArabicShapeThreads threads[30];
51004dcb 216
2ca993e8 217 int32_t i;
b331163b 218
2ca993e8
A
219 logln("-> do TestArabicShapingThreads <- Firing off threads.. ");
220 for(i=0; i < UPRV_LENGTHOF(threads); i++) {
221 if (threads[i].start() != 0) {
222 errln("Error starting thread %d", i);
51004dcb 223 }
51004dcb
A
224 }
225
2ca993e8
A
226 for(i=0; i < UPRV_LENGTHOF(threads); i++) {
227 threads[i].join();
51004dcb 228 }
2ca993e8 229 logln("->TestArabicShapingThreads <- Got all threads! cya");
51004dcb
A
230}
231
b331163b 232
374ca955 233//-------------------------------------------------------------------------------------------
b75a7d8f 234//
2ca993e8 235// TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment
b75a7d8f 236//
374ca955 237//-------------------------------------------------------------------------------------------
b75a7d8f
A
238
239
b75a7d8f
A
240// * Show exactly where the string's differences lie.
241UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result)
242{
243 UnicodeString res;
3d1f044b 244 res = expected + u"<Expected\n";
b75a7d8f 245 if(expected.length() != result.length())
3d1f044b 246 res += u" [ Different lengths ] \n";
b75a7d8f
A
247 else
248 {
249 for(int32_t i=0;i<expected.length();i++)
250 {
251 if(expected[i] == result[i])
252 {
3d1f044b 253 res += u" ";
b75a7d8f
A
254 }
255 else
256 {
3d1f044b 257 res += u"|";
b75a7d8f
A
258 }
259 }
3d1f044b
A
260 res += u"<Differences";
261 res += u"\n";
b75a7d8f 262 }
3d1f044b 263 res += result + u"<Result\n";
b75a7d8f
A
264
265 return res;
266}
267
268
374ca955
A
269//-------------------------------------------------------------------------------------------
270//
271// FormatThreadTest - a thread that tests performing a number of numberformats.
272//
273//-------------------------------------------------------------------------------------------
b75a7d8f 274
57a6839d 275const int kFormatThreadIterations = 100; // # of iterations per thread
b331163b 276const int kFormatThreadThreads = 10; // # of threads to spawn
b75a7d8f
A
277
278#if !UCONFIG_NO_FORMATTING
279
b75a7d8f
A
280
281
282struct FormatThreadTestData
283{
284 double number;
285 UnicodeString string;
286 FormatThreadTestData(double a, const UnicodeString& b) : number(a),string(b) {}
287} ;
288
289
b75a7d8f
A
290// "Someone from {2} is receiving a #{0} error - {1}. Their telephone call is costing {3 number,currency}."
291
57a6839d 292static void formatErrorMessage(UErrorCode &realStatus, const UnicodeString& pattern, const Locale& theLocale,
0f5d89e8
A
293 UErrorCode inStatus0, // statusString 1
294 const Locale &inCountry2, double currency3, // these numbers are the message arguments.
b75a7d8f
A
295 UnicodeString &result)
296{
297 if(U_FAILURE(realStatus))
298 return; // you messed up
299
73c04bcf 300 UnicodeString errString1(u_errorName(inStatus0));
b75a7d8f
A
301
302 UnicodeString countryName2;
303 inCountry2.getDisplayCountry(theLocale,countryName2);
304
305 Formattable myArgs[] = {
306 Formattable((int32_t)inStatus0), // inStatus0 {0}
307 Formattable(errString1), // statusString1 {1}
308 Formattable(countryName2), // inCountry2 {2}
309 Formattable(currency3)// currency3 {3,number,currency}
310 };
311
3d1f044b 312 LocalPointer<MessageFormat> fmt(new MessageFormat(u"MessageFormat's API is broken!!!!!!!!!!!",realStatus), realStatus);
b75a7d8f 313 if (U_FAILURE(realStatus)) {
b75a7d8f
A
314 return;
315 }
3d1f044b
A
316 fmt->setLocale(theLocale);
317 fmt->applyPattern(pattern, realStatus);
b75a7d8f 318
b331163b 319 FieldPosition ignore = 0;
b75a7d8f 320 fmt->format(myArgs,4,result,ignore,realStatus);
73c04bcf 321}
b75a7d8f 322
57a6839d 323/**
b331163b
A
324 * Shared formatters & data used by instances of ThreadSafeFormat.
325 * Exactly one instance of this class is created, and it is then shared concurrently
326 * by the multiple instances of ThreadSafeFormat.
327 */
328class ThreadSafeFormatSharedData {
329 public:
330 ThreadSafeFormatSharedData(UErrorCode &status);
331 ~ThreadSafeFormatSharedData();
332 LocalPointer<NumberFormat> fFormat;
333 Formattable fYDDThing;
334 Formattable fBBDThing;
335 UnicodeString fYDDStr;
336 UnicodeString fBBDStr;
337};
338
339const ThreadSafeFormatSharedData *gSharedData = NULL;
340
341ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) {
342 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
3d1f044b
A
343 static const UChar *kYDD = u"YDD";
344 static const UChar *kBBD = u"BBD";
b331163b
A
345 fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status));
346 fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status));
347 if (U_FAILURE(status)) {
348 return;
349 }
350 fFormat->format(fYDDThing, fYDDStr, NULL, status);
351 fFormat->format(fBBDThing, fBBDStr, NULL, status);
352 gSharedData = this;
353}
354
355ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() {
356 gSharedData = NULL;
357}
358
359/**
360 * Class for thread-safe testing of format.
361 * Instances of this class appear as members of class FormatThreadTest.
362 * Multiple instances of FormatThreadTest coexist.
363 * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of
364 * various shared format operations.
57a6839d
A
365 */
366class ThreadSafeFormat {
367public:
368 /* give a unique offset to each thread */
b331163b
A
369 ThreadSafeFormat(UErrorCode &status);
370 UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const;
57a6839d 371private:
b331163b 372 LocalPointer<NumberFormat> fFormat; // formatter - en_US constructed currency
57a6839d
A
373};
374
57a6839d 375
b331163b
A
376ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) {
377 fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status));
57a6839d
A
378}
379
3d1f044b 380static const UChar *kUSD = u"USD";
57a6839d 381
b331163b 382UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const {
57a6839d 383 UBool okay = TRUE;
57a6839d
A
384
385 if(u_strcmp(fFormat->getCurrency(), kUSD)) {
3d1f044b 386 appendErr.append(u"fFormat currency != ")
57a6839d 387 .append(kUSD)
3d1f044b 388 .append(u", =")
57a6839d 389 .append(fFormat->getCurrency())
3d1f044b 390 .append(u"! ");
57a6839d
A
391 okay = FALSE;
392 }
393
b331163b 394 if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) {
3d1f044b 395 appendErr.append(u"gFormat currency != ")
57a6839d 396 .append(kUSD)
3d1f044b 397 .append(u", =")
b331163b 398 .append(gSharedData->fFormat->getCurrency())
3d1f044b 399 .append(u"! ");
57a6839d
A
400 okay = FALSE;
401 }
402 UnicodeString str;
403 const UnicodeString *o=NULL;
404 Formattable f;
405 const NumberFormat *nf = NULL; // only operate on it as const.
406 switch(offset%4) {
b331163b
A
407 case 0: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = gSharedData->fFormat.getAlias(); break;
408 case 1: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = gSharedData->fFormat.getAlias(); break;
409 case 2: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = fFormat.getAlias(); break;
410 case 3: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = fFormat.getAlias(); break;
57a6839d
A
411 }
412 nf->format(f, str, NULL, status);
413
414 if(*o != str) {
415 appendErr.append(showDifference(*o, str));
416 okay = FALSE;
417 }
418 return okay;
419}
374ca955
A
420
421UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInfo *) {
422 return TRUE;
423}
424
425//static UMTX debugMutex = NULL;
426//static UMTX gDebugMutex;
427
b75a7d8f 428
2ca993e8 429class FormatThreadTest : public SimpleThread
b75a7d8f
A
430{
431public:
374ca955
A
432 int fNum;
433 int fTraceInfo;
434
b331163b 435 LocalPointer<ThreadSafeFormat> fTSF;
57a6839d 436
b75a7d8f 437 FormatThreadTest() // constructor is NOT multithread safe.
2ca993e8 438 : SimpleThread(),
374ca955
A
439 fNum(0),
440 fTraceInfo(0),
b331163b 441 fTSF(NULL),
b75a7d8f
A
442 fOffset(0)
443 // the locale to use
444 {
b331163b
A
445 UErrorCode status = U_ZERO_ERROR; // TODO: rearrange code to allow checking of status.
446 fTSF.adoptInstead(new ThreadSafeFormat(status));
b75a7d8f
A
447 static int32_t fgOffset = 0;
448 fgOffset += 3;
449 fOffset = fgOffset;
450 }
451
452
453 virtual void run()
454 {
374ca955 455 fTraceInfo = 1;
729e4ab9 456 LocalPointer<NumberFormat> percentFormatter;
374ca955
A
457 UErrorCode status = U_ZERO_ERROR;
458
459#if 0
b331163b 460 // debugging code,
374ca955
A
461 for (int i=0; i<4000; i++) {
462 status = U_ZERO_ERROR;
463 UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status);
464 UDataMemory *data2 = udata_openChoice(0, "res", "fr", isAcceptable, 0, &status);
465 udata_close(data1);
466 udata_close(data2);
467 if (U_FAILURE(status)) {
468 error("udata_openChoice failed.\n");
469 break;
470 }
471 }
472 return;
473#endif
474
46f4442e 475#if 0
b331163b 476 // debugging code,
374ca955
A
477 int m;
478 for (m=0; m<4000; m++) {
479 status = U_ZERO_ERROR;
480 UResourceBundle *res = NULL;
481 const char *localeName = NULL;
482
483 Locale loc = Locale::getEnglish();
484
485 localeName = loc.getName();
486 // localeName = "en";
487
488 // ResourceBundle bund = ResourceBundle(0, loc, status);
489 //umtx_lock(&gDebugMutex);
490 res = ures_open(NULL, localeName, &status);
491 //umtx_unlock(&gDebugMutex);
492
493 //umtx_lock(&gDebugMutex);
494 ures_close(res);
495 //umtx_unlock(&gDebugMutex);
496
497 if (U_FAILURE(status)) {
498 error("Resource bundle construction failed.\n");
499 break;
500 }
501 }
502 return;
503#endif
504
b75a7d8f 505 // Keep this data here to avoid static initialization.
b331163b 506 FormatThreadTestData kNumberFormatTestData[] =
b75a7d8f 507 {
0f5d89e8
A
508 FormatThreadTestData((double)5.0, UnicodeString(u"5")),
509 FormatThreadTestData( 6.0, UnicodeString(u"6")),
510 FormatThreadTestData( 20.0, UnicodeString(u"20")),
511 FormatThreadTestData( 8.0, UnicodeString(u"8")),
512 FormatThreadTestData( 8.3, UnicodeString(u"8.3")),
513 FormatThreadTestData( 12345, UnicodeString(u"12,345")),
514 FormatThreadTestData( 81890.23, UnicodeString(u"81,890.23")),
b75a7d8f 515 };
b331163b
A
516 int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData);
517
b75a7d8f 518 // Keep this data here to avoid static initialization.
b331163b 519 FormatThreadTestData kPercentFormatTestData[] =
b75a7d8f 520 {
46f4442e
A
521 FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")),
522 FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")),
523 FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")),
b331163b 524 FormatThreadTestData(
3d1f044b 525 16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP
b331163b 526 FormatThreadTestData(
3d1f044b 527 81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")),
b75a7d8f 528 };
b331163b 529 int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData);
b75a7d8f 530 int32_t iteration;
b331163b 531
374ca955 532 status = U_ZERO_ERROR;
729e4ab9 533 LocalPointer<NumberFormat> formatter(NumberFormat::createInstance(Locale::getEnglish(),status));
374ca955 534 if(U_FAILURE(status)) {
2ca993e8
A
535 IntlTest::gTest->dataerrln("%s:%d Error %s on NumberFormat::createInstance().",
536 __FILE__, __LINE__, u_errorName(status));
374ca955 537 goto cleanupAndReturn;
b75a7d8f 538 }
b331163b 539
729e4ab9 540 percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status));
374ca955 541 if(U_FAILURE(status)) {
2ca993e8
A
542 IntlTest::gTest->errln("%s:%d Error %s on NumberFormat::createPercentInstance().",
543 __FILE__, __LINE__, u_errorName(status));
374ca955 544 goto cleanupAndReturn;
b75a7d8f 545 }
b331163b 546
2ca993e8 547 for(iteration = 0;!IntlTest::gTest->getErrors() && iteration<kFormatThreadIterations;iteration++)
b75a7d8f 548 {
b331163b 549
b75a7d8f 550 int32_t whichLine = (iteration + fOffset)%kNumberFormatTestDataLength;
b331163b 551
b75a7d8f 552 UnicodeString output;
b331163b 553
b75a7d8f 554 formatter->format(kNumberFormatTestData[whichLine].number, output);
b331163b 555
374ca955 556 if(0 != output.compare(kNumberFormatTestData[whichLine].string)) {
2ca993e8 557 IntlTest::gTest->errln("format().. expected " + kNumberFormatTestData[whichLine].string
374ca955
A
558 + " got " + output);
559 goto cleanupAndReturn;
b75a7d8f 560 }
b331163b 561
b75a7d8f
A
562 // Now check percent.
563 output.remove();
564 whichLine = (iteration + fOffset)%kPercentFormatTestDataLength;
b331163b 565
b75a7d8f 566 percentFormatter->format(kPercentFormatTestData[whichLine].number, output);
b75a7d8f
A
567 if(0 != output.compare(kPercentFormatTestData[whichLine].string))
568 {
2ca993e8 569 IntlTest::gTest->errln("percent format().. \n" +
374ca955
A
570 showDifference(kPercentFormatTestData[whichLine].string,output));
571 goto cleanupAndReturn;
b75a7d8f 572 }
b331163b
A
573
574 // Test message error
374ca955 575 const int kNumberOfMessageTests = 3;
b75a7d8f
A
576 UErrorCode statusToCheck;
577 UnicodeString patternToCheck;
578 Locale messageLocale;
579 Locale countryToCheck;
580 double currencyToCheck;
b331163b 581
b75a7d8f 582 UnicodeString expected;
b331163b 583
b75a7d8f
A
584 // load the cases.
585 switch((iteration+fOffset) % kNumberOfMessageTests)
586 {
587 default:
588 case 0:
589 statusToCheck= U_FILE_ACCESS_ERROR;
3d1f044b
A
590 patternToCheck= u"0:Someone from {2} is receiving a #{0}"
591 " error - {1}. Their telephone call is costing "
592 "{3,number,currency}."; // number,currency
b75a7d8f
A
593 messageLocale= Locale("en","US");
594 countryToCheck= Locale("","HR");
595 currencyToCheck= 8192.77;
3d1f044b 596 expected= u"0:Someone from Croatia is receiving a #4 error - "
374ca955 597 "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77.";
b75a7d8f
A
598 break;
599 case 1:
600 statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR;
3d1f044b
A
601 patternToCheck= u"1:A customer in {2} is receiving a #{0} error - {1}. "
602 "Their telephone call is costing {3,number,currency}."; // number,currency
374ca955 603 messageLocale= Locale("de","DE@currency=DEM");
b75a7d8f
A
604 countryToCheck= Locale("","BF");
605 currencyToCheck= 2.32;
3d1f044b
A
606 expected= u"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. "
607 u"Their telephone call is costing 2,32\u00A0DM.";
374ca955 608 break;
b75a7d8f
A
609 case 2:
610 statusToCheck= U_MEMORY_ALLOCATION_ERROR;
3d1f044b 611 patternToCheck= u"2:user in {2} is receiving a #{0} error - {1}. "
374ca955
A
612 "They insist they just spent {3,number,currency} "
613 "on memory."; // number,currency
614 messageLocale= Locale("de","AT@currency=ATS"); // Austrian German
b75a7d8f
A
615 countryToCheck= Locale("","US"); // hmm
616 currencyToCheck= 40193.12;
3d1f044b
A
617 expected= u"2:user in Vereinigte Staaten is receiving a #7 error"
618 u" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent"
619 u" \u00f6S\u00A040.193,12 on memory.";
b75a7d8f
A
620 break;
621 }
b331163b 622
b75a7d8f
A
623 UnicodeString result;
624 UErrorCode status = U_ZERO_ERROR;
374ca955
A
625 formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck,
626 countryToCheck,currencyToCheck,result);
b75a7d8f
A
627 if(U_FAILURE(status))
628 {
73c04bcf 629 UnicodeString tmp(u_errorName(status));
3d1f044b 630 IntlTest::gTest->errln(u"Failure on message format, pattern=" + patternToCheck +
374ca955
A
631 ", error = " + tmp);
632 goto cleanupAndReturn;
b75a7d8f 633 }
b331163b 634
b75a7d8f
A
635 if(result != expected)
636 {
3d1f044b 637 IntlTest::gTest->errln(u"PatternFormat: \n" + showDifference(expected,result));
374ca955 638 goto cleanupAndReturn;
b75a7d8f 639 }
57a6839d
A
640 // test the Thread Safe Format
641 UnicodeString appendErr;
b331163b 642 if(!fTSF->doStuff(fNum, appendErr, status)) {
2ca993e8 643 IntlTest::gTest->errln(appendErr);
57a6839d
A
644 goto cleanupAndReturn;
645 }
374ca955 646 } /* end of for loop */
57a6839d
A
647
648
649
374ca955 650cleanupAndReturn:
374ca955 651 fTraceInfo = 2;
b75a7d8f 652 }
b331163b 653
b75a7d8f
A
654private:
655 int32_t fOffset; // where we are testing from.
656};
657
658// ** The actual test function.
659
660void MultithreadTest::TestThreadedIntl()
661{
374ca955 662 UnicodeString theErr;
374ca955 663
57a6839d
A
664 UErrorCode threadSafeErr = U_ZERO_ERROR;
665
b331163b 666 ThreadSafeFormatSharedData sharedData(threadSafeErr);
3d1f044b 667 assertSuccess(WHERE, threadSafeErr, TRUE);
57a6839d 668
374ca955
A
669 //
670 // Create and start the test threads
671 //
672 logln("Spawning: %d threads * %d iterations each.",
673 kFormatThreadThreads, kFormatThreadIterations);
2ca993e8
A
674 FormatThreadTest tests[kFormatThreadThreads];
675 int32_t j;
676 for(j = 0; j < UPRV_LENGTHOF(tests); j++) {
374ca955 677 tests[j].fNum = j;
b75a7d8f
A
678 int32_t threadStatus = tests[j].start();
679 if (threadStatus != 0) {
2ca993e8
A
680 errln("%s:%d System Error %d starting thread number %d.",
681 __FILE__, __LINE__, threadStatus, j);
729e4ab9 682 return;
b75a7d8f 683 }
2ca993e8 684 }
b75a7d8f 685
2ca993e8
A
686
687 for (j=0; j<UPRV_LENGTHOF(tests); j++) {
688 tests[j].join();
689 logln("Thread # %d is complete..", j);
690 }
374ca955
A
691}
692#endif /* #if !UCONFIG_NO_FORMATTING */
b75a7d8f 693
b75a7d8f 694
b75a7d8f 695
b75a7d8f 696
b75a7d8f 697
374ca955
A
698//-------------------------------------------------------------------------------------------
699//
700// Collation threading test
701//
702//-------------------------------------------------------------------------------------------
b75a7d8f
A
703#if !UCONFIG_NO_COLLATION
704
705#define kCollatorThreadThreads 10 // # of threads to spawn
73c04bcf 706#define kCollatorThreadPatience kCollatorThreadThreads*30
b75a7d8f
A
707
708struct Line {
374ca955
A
709 UChar buff[25];
710 int32_t buflen;
b75a7d8f
A
711} ;
712
51004dcb
A
713
714static UCollationResult
715normalizeResult(int32_t result) {
716 return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER;
717}
718
2ca993e8 719class CollatorThreadTest : public SimpleThread
b75a7d8f 720{
b331163b 721private:
51004dcb 722 const Collator *coll;
374ca955
A
723 const Line *lines;
724 int32_t noLines;
51004dcb 725 UBool isAtLeastUCA62;
b75a7d8f 726public:
2ca993e8 727 CollatorThreadTest() : SimpleThread(),
374ca955
A
728 coll(NULL),
729 lines(NULL),
51004dcb
A
730 noLines(0),
731 isAtLeastUCA62(TRUE)
374ca955 732 {
340931cb 733 }
51004dcb 734 void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62)
374ca955
A
735 {
736 coll = c;
737 lines = l;
738 noLines = nl;
51004dcb 739 isAtLeastUCA62 = atLeastUCA62;
374ca955
A
740 }
741 virtual void run() {
374ca955
A
742 uint8_t sk1[1024], sk2[1024];
743 uint8_t *oldSk = NULL, *newSk = sk1;
51004dcb
A
744 int32_t oldLen = 0;
745 int32_t prev = 0;
374ca955 746 int32_t i = 0;
729e4ab9 747
374ca955 748 for(i = 0; i < noLines; i++) {
51004dcb
A
749 if(lines[i].buflen == 0) { continue; }
750
51004dcb 751 int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024);
729e4ab9 752
374ca955 753 if(oldSk != NULL) {
51004dcb
A
754 int32_t skres = strcmp((char *)oldSk, (char *)newSk);
755 int32_t cmpres = coll->compare(lines[prev].buff, lines[prev].buflen, lines[i].buff, lines[i].buflen);
756 int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen);
729e4ab9 757
374ca955 758 if(cmpres != -cmpres2) {
3d1f044b 759 IntlTest::gTest->errln(UnicodeString(u"Compare result not symmetrical on line ") + (i + 1));
374ca955
A
760 break;
761 }
729e4ab9 762
51004dcb 763 if(cmpres != normalizeResult(skres)) {
3d1f044b 764 IntlTest::gTest->errln(UnicodeString(u"Difference between coll->compare and sortkey compare on line ") + (i + 1));
374ca955
A
765 break;
766 }
729e4ab9 767
51004dcb
A
768 int32_t res = cmpres;
769 if(res == 0 && !isAtLeastUCA62) {
770 // Up to UCA 6.1, the collation test files use a custom tie-breaker,
771 // comparing the raw input strings.
772 res = u_strcmpCodePointOrder(lines[prev].buff, lines[i].buff);
773 // Starting with UCA 6.2, the collation test files use the standard UCA tie-breaker,
774 // comparing the NFD versions of the input strings,
775 // which we do via setting strength=identical.
776 }
374ca955 777 if(res > 0) {
3d1f044b 778 IntlTest::gTest->errln(UnicodeString(u"Line is not greater or equal than previous line, for line ") + (i + 1));
374ca955 779 break;
374ca955
A
780 }
781 }
729e4ab9 782
374ca955
A
783 oldSk = newSk;
784 oldLen = resLen;
57a6839d 785 (void)oldLen; // Suppress set but not used warning.
51004dcb 786 prev = i;
729e4ab9 787
374ca955 788 newSk = (newSk == sk1)?sk2:sk1;
b75a7d8f 789 }
b75a7d8f 790 }
b75a7d8f
A
791};
792
793void MultithreadTest::TestCollators()
794{
795
374ca955
A
796 UErrorCode status = U_ZERO_ERROR;
797 FILE *testFile = NULL;
798 char testDataPath[1024];
799 strcpy(testDataPath, IntlTest::getSourceTestData(status));
800 if (U_FAILURE(status)) {
801 errln("ERROR: could not open test data %s", u_errorName(status));
802 return;
803 }
804 strcat(testDataPath, "CollationTest_");
b75a7d8f 805
374ca955 806 const char* type = "NON_IGNORABLE";
b75a7d8f 807
374ca955
A
808 const char *ext = ".txt";
809 if(testFile) {
810 fclose(testFile);
811 }
812 char buffer[1024];
813 strcpy(buffer, testDataPath);
814 strcat(buffer, type);
815 size_t bufLen = strlen(buffer);
b75a7d8f 816
374ca955
A
817 // we try to open 3 files:
818 // path/CollationTest_type.txt
819 // path/CollationTest_type_SHORT.txt
820 // path/CollationTest_type_STUB.txt
821 // we are going to test with the first one that we manage to open.
b75a7d8f 822
374ca955 823 strcpy(buffer+bufLen, ext);
b75a7d8f 824
b75a7d8f
A
825 testFile = fopen(buffer, "rb");
826
827 if(testFile == 0) {
374ca955
A
828 strcpy(buffer+bufLen, "_SHORT");
829 strcat(buffer, ext);
830 testFile = fopen(buffer, "rb");
831
832 if(testFile == 0) {
833 strcpy(buffer+bufLen, "_STUB");
834 strcat(buffer, ext);
835 testFile = fopen(buffer, "rb");
836
837 if (testFile == 0) {
838 *(buffer+bufLen) = 0;
729e4ab9 839 dataerrln("could not open any of the conformance test files, tried opening base %s", buffer);
b331163b 840 return;
374ca955
A
841 } else {
842 infoln(
843 "INFO: Working with the stub file.\n"
844 "If you need the full conformance test, please\n"
845 "download the appropriate data files from:\n"
46f4442e 846 "http://source.icu-project.org/repos/icu/tools/trunk/unicodetools/com/ibm/text/data/");
374ca955
A
847 }
848 }
b75a7d8f 849 }
b75a7d8f 850
51004dcb
A
851 LocalArray<Line> lines(new Line[200000]);
852 memset(lines.getAlias(), 0, sizeof(Line)*200000);
374ca955 853 int32_t lineNum = 0;
b75a7d8f 854
374ca955 855 UChar bufferU[1024];
374ca955 856 uint32_t first = 0;
b75a7d8f 857
374ca955 858 while (fgets(buffer, 1024, testFile) != NULL) {
51004dcb
A
859 if(*buffer == 0 || buffer[0] == '#') {
860 // Store empty and comment lines so that errors are reported
861 // for the real test file lines.
862 lines[lineNum].buflen = 0;
863 lines[lineNum].buff[0] = 0;
864 } else {
865 int32_t buflen = u_parseString(buffer, bufferU, 1024, &first, &status);
866 lines[lineNum].buflen = buflen;
867 u_memcpy(lines[lineNum].buff, bufferU, buflen);
868 lines[lineNum].buff[buflen] = 0;
374ca955 869 }
374ca955 870 lineNum++;
b75a7d8f 871 }
374ca955 872 fclose(testFile);
73c04bcf 873 if(U_FAILURE(status)) {
729e4ab9 874 dataerrln("Couldn't read the test file!");
73c04bcf
A
875 return;
876 }
374ca955 877
51004dcb
A
878 UVersionInfo uniVersion;
879 static const UVersionInfo v62 = { 6, 2, 0, 0 };
880 u_getUnicodeVersion(uniVersion);
881 UBool isAtLeastUCA62 = uprv_memcmp(uniVersion, v62, 4) >= 0;
882
883 LocalPointer<Collator> coll(Collator::createInstance(Locale::getRoot(), status));
374ca955 884 if(U_FAILURE(status)) {
729e4ab9 885 errcheckln(status, "Couldn't open UCA collator");
374ca955
A
886 return;
887 }
51004dcb
A
888 coll->setAttribute(UCOL_NORMALIZATION_MODE, UCOL_ON, status);
889 coll->setAttribute(UCOL_CASE_FIRST, UCOL_OFF, status);
890 coll->setAttribute(UCOL_CASE_LEVEL, UCOL_OFF, status);
891 coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TERTIARY, status);
892 coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status);
374ca955 893
374ca955 894 int32_t spawnResult = 0;
729e4ab9 895 LocalArray<CollatorThreadTest> tests(new CollatorThreadTest[kCollatorThreadThreads]);
374ca955 896
3d1f044b 897 logln(UnicodeString(u"Spawning: ") + kCollatorThreadThreads + u" threads * " + kFormatThreadIterations + u" iterations each.");
b75a7d8f
A
898 int32_t j = 0;
899 for(j = 0; j < kCollatorThreadThreads; j++) {
374ca955 900 //logln("Setting collator %i", j);
51004dcb 901 tests[j].setCollator(coll.getAlias(), lines.getAlias(), lineNum, isAtLeastUCA62);
b75a7d8f
A
902 }
903 for(j = 0; j < kCollatorThreadThreads; j++) {
374ca955
A
904 log("%i ", j);
905 spawnResult = tests[j].start();
906 if(spawnResult != 0) {
2ca993e8
A
907 errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__, __LINE__, j, spawnResult);
908 return;
374ca955 909 }
b75a7d8f
A
910 }
911 logln("Spawned all");
b75a7d8f 912
2ca993e8
A
913 for(int32_t i=0;i<kCollatorThreadThreads;i++) {
914 tests[i].join();
915 //logln(UnicodeString("Test #") + i + " is complete.. ");
b75a7d8f 916 }
b75a7d8f
A
917}
918
919#endif /* #if !UCONFIG_NO_COLLATION */
920
374ca955
A
921
922
923
924//-------------------------------------------------------------------------------------------
925//
b331163b 926// StringThreadTest2
374ca955
A
927//
928//-------------------------------------------------------------------------------------------
929
930const int kStringThreadIterations = 2500;// # of iterations per thread
931const int kStringThreadThreads = 10; // # of threads to spawn
374ca955
A
932
933
2ca993e8 934class StringThreadTest2 : public SimpleThread
374ca955
A
935{
936public:
937 int fNum;
938 int fTraceInfo;
2ca993e8 939 static const UnicodeString *gSharedString;
374ca955 940
2ca993e8
A
941 StringThreadTest2() // constructor is NOT multithread safe.
942 : SimpleThread(),
943 fTraceInfo(0)
374ca955 944 {
340931cb 945 }
374ca955
A
946
947
948 virtual void run()
949 {
950 fTraceInfo = 1;
951 int loopCount = 0;
952
953 for (loopCount = 0; loopCount < kStringThreadIterations; loopCount++) {
3d1f044b 954 if (*gSharedString != u"This is the original test string.") {
2ca993e8 955 IntlTest::gTest->errln("%s:%d Original string is corrupt.", __FILE__, __LINE__);
374ca955
A
956 break;
957 }
2ca993e8 958 UnicodeString s1 = *gSharedString;
3d1f044b 959 s1 += u"cat this";
374ca955 960 UnicodeString s2(s1);
2ca993e8 961 UnicodeString s3 = *gSharedString;
374ca955
A
962 s2 = s3;
963 s3.truncate(12);
964 s2.truncate(0);
965 }
966
374ca955
A
967 fTraceInfo = 2;
968 }
b331163b 969
374ca955
A
970};
971
2ca993e8
A
972const UnicodeString *StringThreadTest2::gSharedString = NULL;
973
374ca955
A
974// ** The actual test function.
975
2ca993e8 976
374ca955
A
977void MultithreadTest::TestString()
978{
374ca955 979 int j;
3d1f044b 980 StringThreadTest2::gSharedString = new UnicodeString(u"This is the original test string.");
2ca993e8 981 StringThreadTest2 tests[kStringThreadThreads];
b331163b 982
3d1f044b 983 logln(UnicodeString(u"Spawning: ") + kStringThreadThreads + u" threads * " + kStringThreadIterations + u" iterations each.");
374ca955 984 for(j = 0; j < kStringThreadThreads; j++) {
2ca993e8 985 int32_t threadStatus = tests[j].start();
374ca955 986 if (threadStatus != 0) {
2ca993e8 987 errln("%s:%d System Error %d starting thread number %d.", __FILE__, __LINE__, threadStatus, j);
374ca955
A
988 }
989 }
990
2ca993e8
A
991 // Force a failure, to verify test is functioning and can report errors.
992 // const_cast<UnicodeString *>(StringThreadTest2::gSharedString)->setCharAt(5, 'x');
b331163b 993
2ca993e8
A
994 for(j=0; j<kStringThreadThreads; j++) {
995 tests[j].join();
3d1f044b 996 logln(UnicodeString(u"Test #") + j + u" is complete.. ");
374ca955
A
997 }
998
2ca993e8
A
999 delete StringThreadTest2::gSharedString;
1000 StringThreadTest2::gSharedString = NULL;
374ca955
A
1001}
1002
57a6839d 1003
2ca993e8 1004//
57a6839d
A
1005// Test for ticket #10673, race in cache code in AnyTransliterator.
1006// It's difficult to make the original unsafe code actually fail, but
b331163b 1007// this test will fairly reliably take the code path for races in
57a6839d 1008// populating the cache.
2ca993e8 1009//
57a6839d
A
1010
1011#if !UCONFIG_NO_TRANSLITERATION
2ca993e8 1012Transliterator *gSharedTranslit = NULL;
57a6839d 1013class TxThread: public SimpleThread {
57a6839d 1014 public:
340931cb 1015 TxThread() {}
57a6839d
A
1016 ~TxThread();
1017 void run();
1018};
1019
1020TxThread::~TxThread() {}
1021void TxThread::run() {
3d1f044b 1022 UnicodeString greekString(u"διαφορετικούς");
2ca993e8 1023 gSharedTranslit->transliterate(greekString);
3d1f044b 1024 IntlTest::gTest->assertEquals(WHERE, UnicodeString(u"diaphoretikoús"), greekString);
57a6839d
A
1025}
1026#endif
b331163b 1027
57a6839d
A
1028
1029void MultithreadTest::TestAnyTranslit() {
1030#if !UCONFIG_NO_TRANSLITERATION
1031 UErrorCode status = U_ZERO_ERROR;
1032 LocalPointer<Transliterator> tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status));
3d1f044b
A
1033 if (!assertSuccess(WHERE, status, true)) { return; }
1034
2ca993e8
A
1035 gSharedTranslit = tx.getAlias();
1036 TxThread threads[4];
57a6839d 1037 int32_t i;
2ca993e8
A
1038 for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1039 threads[i].start();
57a6839d 1040 }
57a6839d 1041
2ca993e8
A
1042 for (i=0; i<UPRV_LENGTHOF(threads); i++) {
1043 threads[i].join();
57a6839d 1044 }
2ca993e8 1045 gSharedTranslit = NULL;
57a6839d
A
1046#endif // !UCONFIG_NO_TRANSLITERATION
1047}
1048
b331163b 1049
2ca993e8
A
1050
1051//
1052// Unified Cache Test
1053//
1054
1055// Each thread fetches a pair of objects. There are 8 distinct pairs:
1056// ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc.
1057// These pairs represent 8 distinct languages
1058
1059// Note that only one value per language gets created in the cache.
1060// In particular each cached value can have multiple keys.
1061static const char *gCacheLocales[] = {
1062 "en_US", "en_GB", "fr_FR", "fr",
1063 "de", "sr_ME", "sr_BA", "sr_CS"};
1064static const char *gCacheLocales2[] = {
1065 "bs", "ca", "ca_AD", "ca_ES",
1066 "en_US", "fi", "ff_CM", "ff_GN"};
1067
1068static int32_t gObjectsCreated = 0; // protected by gCTMutex
b331163b
A
1069static const int32_t CACHE_LOAD = 3;
1070
1071class UCTMultiThreadItem : public SharedObject {
1072 public:
1073 char *value;
1074 UCTMultiThreadItem(const char *x) : value(NULL) {
1075 value = uprv_strdup(x);
1076 }
1077 virtual ~UCTMultiThreadItem() {
1078 uprv_free(value);
1079 }
1080};
1081
1082U_NAMESPACE_BEGIN
1083
340931cb
A
1084static std::mutex *gCTMutex = nullptr;
1085static std::condition_variable *gCTConditionVar = nullptr;
1086
b331163b
A
1087template<> U_EXPORT
1088const UCTMultiThreadItem *LocaleCacheKey<UCTMultiThreadItem>::createObject(
2ca993e8
A
1089 const void *context, UErrorCode &status) const {
1090 const UnifiedCache *cacheContext = (const UnifiedCache *) context;
1091
1092 if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) {
1093 const UCTMultiThreadItem *result = NULL;
1094 if (cacheContext == NULL) {
1095 UnifiedCache::getByLocale(fLoc.getLanguage(), result, status);
1096 return result;
1097 }
1098 cacheContext->get(LocaleCacheKey<UCTMultiThreadItem>(fLoc.getLanguage()), result, status);
1099 return result;
1100 }
1101
3d1f044b
A
1102 bool firstObject = false;
1103 {
340931cb 1104 std::unique_lock<std::mutex> lock(*gCTMutex);
3d1f044b
A
1105 firstObject = (gObjectsCreated == 0);
1106 if (firstObject) {
1107 // Force the first object creation that comes through to wait
1108 // until other have completed. Verifies that cache doesn't
1109 // deadlock when a creation is slow.
1110
1111 // Note that gObjectsCreated needs to be incremeneted from 0 to 1
1112 // early, to keep subsequent threads from entering this path.
1113 gObjectsCreated = 1;
1114 while (gObjectsCreated < 3) {
340931cb 1115 gCTConditionVar->wait(lock);
3d1f044b 1116 }
2ca993e8 1117 }
b331163b 1118 }
b331163b 1119
2ca993e8
A
1120 const UCTMultiThreadItem *result =
1121 new UCTMultiThreadItem(fLoc.getLanguage());
1122 if (result == NULL) {
1123 status = U_MEMORY_ALLOCATION_ERROR;
1124 } else {
1125 result->addRef();
1126 }
0f5d89e8 1127
2ca993e8
A
1128 // Log that we created an object. The first object was already counted,
1129 // don't do it again.
3d1f044b 1130 {
340931cb 1131 std::unique_lock<std::mutex> lock(*gCTMutex);
3d1f044b
A
1132 if (!firstObject) {
1133 gObjectsCreated += 1;
1134 }
340931cb 1135 gCTConditionVar->notify_all();
2ca993e8 1136 }
2ca993e8 1137
b331163b
A
1138 return result;
1139}
1140
1141U_NAMESPACE_END
1142
1143class UnifiedCacheThread: public SimpleThread {
1144 public:
2ca993e8
A
1145 UnifiedCacheThread(
1146 const UnifiedCache *cache,
1147 const char *loc,
340931cb
A
1148 const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {}
1149 ~UnifiedCacheThread() {}
b331163b 1150 void run();
2ca993e8
A
1151 void exerciseByLocale(const Locale &);
1152 const UnifiedCache *fCache;
1153 Locale fLoc;
1154 Locale fLoc2;
b331163b
A
1155};
1156
2ca993e8 1157void UnifiedCacheThread::exerciseByLocale(const Locale &locale) {
b331163b 1158 UErrorCode status = U_ZERO_ERROR;
2ca993e8
A
1159 const UCTMultiThreadItem *origItem = NULL;
1160 fCache->get(
1161 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, origItem, status);
1162 U_ASSERT(U_SUCCESS(status));
3d1f044b 1163 IntlTest::gTest->assertEquals(WHERE, locale.getLanguage(), origItem->value);
2ca993e8
A
1164
1165 // Fetch the same item again many times. We should always get the same
1166 // pointer since this client is already holding onto it
1167 for (int32_t i = 0; i < 1000; ++i) {
1168 const UCTMultiThreadItem *item = NULL;
1169 fCache->get(
1170 LocaleCacheKey<UCTMultiThreadItem>(locale), fCache, item, status);
3d1f044b 1171 IntlTest::gTest->assertTrue(WHERE, item == origItem);
2ca993e8
A
1172 if (item != NULL) {
1173 item->removeRef();
1174 }
b331163b 1175 }
2ca993e8
A
1176 origItem->removeRef();
1177}
b331163b 1178
2ca993e8
A
1179void UnifiedCacheThread::run() {
1180 // Run the exercise with 2 different locales so that we can exercise
0f5d89e8 1181 // eviction more. If each thread exercises just one locale, then
2ca993e8
A
1182 // eviction can't start until the threads end.
1183 exerciseByLocale(fLoc);
1184 exerciseByLocale(fLoc2);
b331163b
A
1185}
1186
1187void MultithreadTest::TestUnifiedCache() {
2ca993e8
A
1188
1189 // Start with our own local cache so that we have complete control
1190 // and set the eviction policy to evict starting with 2 unused
1191 // values
b331163b 1192 UErrorCode status = U_ZERO_ERROR;
2ca993e8
A
1193 UnifiedCache::getInstance(status);
1194 UnifiedCache cache(status);
1195 cache.setEvictionPolicy(2, 0, status);
1196 U_ASSERT(U_SUCCESS(status));
1197
340931cb
A
1198 gCTMutex = new std::mutex();
1199 gCTConditionVar = new std::condition_variable();
1200
b331163b
A
1201 gObjectsCreated = 0;
1202
1203 UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)];
1204 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1205 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
2ca993e8
A
1206 // Each thread works with a pair of locales.
1207 threads[i][j] = new UnifiedCacheThread(
1208 &cache, gCacheLocales[j], gCacheLocales2[j]);
b331163b
A
1209 threads[i][j]->start();
1210 }
1211 }
2ca993e8
A
1212
1213 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1214 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1215 threads[i][j]->join();
1216 }
b331163b 1217 }
2ca993e8
A
1218 // Because of cache eviction, we can't assert exactly how many
1219 // distinct objects get created over the course of this run.
1220 // However we know that at least 8 objects get created because that
1221 // is how many distinct languages we have in our test.
1222 if (gObjectsCreated < 8) {
1223 errln("%s:%d Too few objects created.", __FILE__, __LINE__);
1224 }
1225 // We know that each thread cannot create more than 2 objects in
1226 // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of
1227 // objects fetched from the cache. If the threads run in series because
1228 // of eviction, at worst case each thread creates two objects.
1229 if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) {
1230 errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, __LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales));
1231
1232 }
1233
3d1f044b 1234 assertEquals(WHERE, 2, cache.unusedCount());
b331163b
A
1235
1236 // clean up threads
1237 for (int32_t i=0; i<CACHE_LOAD; ++i) {
1238 for (int32_t j=0; j<UPRV_LENGTHOF(gCacheLocales); ++j) {
1239 delete threads[i][j];
1240 }
1241 }
340931cb
A
1242 delete gCTMutex;
1243 delete gCTConditionVar;
b331163b
A
1244}
1245
2ca993e8
A
1246#if !UCONFIG_NO_TRANSLITERATION
1247//
1248// BreakTransliterator Threading Test
1249// This is a test for bug #11603. Test verified to fail prior to fix.
1250//
1251
1252static const Transliterator *gSharedTransliterator;
1253static const UnicodeString *gTranslitInput;
1254static const UnicodeString *gTranslitExpected;
1255
1256class BreakTranslitThread: public SimpleThread {
1257 public:
340931cb
A
1258 BreakTranslitThread() {}
1259 ~BreakTranslitThread() {}
2ca993e8
A
1260 void run();
1261};
1262
1263void BreakTranslitThread::run() {
1264 for (int i=0; i<10; i++) {
1265 icu::UnicodeString s(*gTranslitInput);
1266 gSharedTransliterator->transliterate(s);
1267 if (*gTranslitExpected != s) {
1268 IntlTest::gTest->errln("%s:%d Transliteration threading failure.", __FILE__, __LINE__);
1269 break;
1270 }
1271 }
1272}
1273
1274void MultithreadTest::TestBreakTranslit() {
1275 UErrorCode status = U_ZERO_ERROR;
1276 UnicodeString input(
3d1f044b
A
1277 u"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,");
1278 // Thai script, โดยพื้นฐานแล้ว
2ca993e8
A
1279 gTranslitInput = &input;
1280
1281 gSharedTransliterator = Transliterator::createInstance(
3d1f044b
A
1282 UnicodeString(u"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status);
1283 assertSuccess(WHERE, status);
1284 if (!assertTrue(WHERE, gSharedTransliterator != nullptr)) {
1285 return;
1286 }
2ca993e8
A
1287
1288 UnicodeString expected(*gTranslitInput);
1289 gSharedTransliterator->transliterate(expected);
1290 gTranslitExpected = &expected;
1291
1292 BreakTranslitThread threads[4];
1293 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1294 threads[i].start();
1295 }
1296 for (int i=0; i<UPRV_LENGTHOF(threads); ++i) {
1297 threads[i].join();
1298 }
1299
1300 delete gSharedTransliterator;
1301 gTranslitInput = NULL;
1302 gTranslitExpected = NULL;
1303}
1304
0f5d89e8
A
1305
1306class TestIncDecThread : public SimpleThread {
1307public:
340931cb 1308 TestIncDecThread() {}
0f5d89e8
A
1309 virtual void run();
1310};
1311
1312static u_atomic_int32_t gIncDecCounter;
1313
1314void TestIncDecThread::run() {
1315 umtx_atomic_inc(&gIncDecCounter);
1316 for (int32_t i=0; i<5000000; ++i) {
1317 umtx_atomic_inc(&gIncDecCounter);
1318 umtx_atomic_dec(&gIncDecCounter);
1319 }
1320}
1321
1322void MultithreadTest::TestIncDec()
1323{
1324 static constexpr int NUM_THREADS = 4;
1325 gIncDecCounter = 0;
1326 TestIncDecThread threads[NUM_THREADS];
1327 for (auto &thread:threads) {
1328 thread.start();
1329 }
1330 for (auto &thread:threads) {
1331 thread.join();
1332 }
3d1f044b
A
1333 assertEquals(WHERE, NUM_THREADS, gIncDecCounter);
1334}
1335
1336#if !UCONFIG_NO_FORMATTING
1337static Calendar *gSharedCalendar = {};
1338
1339class Test20104Thread : public SimpleThread {
1340public:
340931cb 1341 Test20104Thread() {}
3d1f044b
A
1342 virtual void run();
1343};
1344
1345void Test20104Thread::run() {
1346 gSharedCalendar->defaultCenturyStartYear();
0f5d89e8
A
1347}
1348
3d1f044b
A
1349void MultithreadTest::Test20104() {
1350 UErrorCode status = U_ZERO_ERROR;
1351 Locale loc("hi_IN");
1352 gSharedCalendar = new IndianCalendar(loc, status);
1353 assertSuccess(WHERE, status);
1354
1355 static constexpr int NUM_THREADS = 4;
1356 Test20104Thread threads[NUM_THREADS];
1357 for (auto &thread:threads) {
1358 thread.start();
1359 }
1360 for (auto &thread:threads) {
1361 thread.join();
1362 }
1363 delete gSharedCalendar;
1364 // Note: failure is reported by Thread Sanitizer. Test itself succeeds.
1365}
1366#endif /* !UCONFIG_NO_FORMATTING */
0f5d89e8 1367
2ca993e8 1368#endif /* !UCONFIG_NO_TRANSLITERATION */