1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 3 /** 4 ******************************************************************************* 5 * Copyright (C) 2001-2013, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ******************************************************************************* 8 */ 9 package com.ibm.icu.dev.test.util; 10 11 import java.text.Collator; 12 import java.util.ArrayList; 13 import java.util.Arrays; 14 import java.util.Collection; 15 import java.util.Comparator; 16 import java.util.HashSet; 17 import java.util.Iterator; 18 import java.util.List; 19 import java.util.Locale; 20 import java.util.Map; 21 import java.util.Map.Entry; 22 import java.util.MissingResourceException; 23 import java.util.Random; 24 import java.util.Set; 25 import java.util.SortedMap; 26 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.junit.runners.JUnit4; 30 31 import com.ibm.icu.dev.test.TestFmwk; 32 import com.ibm.icu.impl.ICULocaleService; 33 import com.ibm.icu.impl.ICUService; 34 import com.ibm.icu.impl.ICUService.Factory; 35 import com.ibm.icu.impl.ICUService.SimpleFactory; 36 import com.ibm.icu.util.ULocale; 37 38 @RunWith(JUnit4.class) 39 public class ICUServiceThreadTest extends TestFmwk 40 { 41 private static final boolean PRINTSTATS = false; 42 43 private static final String[] countries = { 44 "ab", "bc", "cd", "de", "ef", "fg", "gh", "ji", "ij", "jk" 45 }; 46 private static final String[] languages = { 47 "", "ZY", "YX", "XW", "WV", "VU", "UT", "TS", "SR", "RQ", "QP" 48 }; 49 private static final String[] variants = { 50 "", "", "", "GOLD", "SILVER", "BRONZE" 51 }; 52 53 private static class TestFactory extends SimpleFactory { 54 TestFactory(String id) { 55 super(new ULocale(id), id, true); 56 } 57 58 @Override 59 public String getDisplayName(String idForDisplay, ULocale locale) { 60 return (visible && idForDisplay.equals(this.id)) ? "(" + locale.toString() + ") " + idForDisplay : null; 61 } 62 63 @Override 64 public String toString() { 65 return "Factory_" + id; 66 } 67 } 68 /** 69 * Convenience override of getDisplayNames(ULocale, Comparator, String) that 70 * uses the default collator for the locale as the comparator to 71 * sort the display names, and null for the matchID. 72 */ 73 public static SortedMap getDisplayNames(ICUService service, ULocale locale) { 74 Collator col; 75 try { 76 col = Collator.getInstance(locale.toLocale()); 77 } 78 catch (MissingResourceException e) { 79 // if no collator resources, we can't collate 80 col = null; 81 } 82 return service.getDisplayNames(locale, col, null); 83 } 84 private static final Random r = new Random(); // this is a multi thread test, can't 'unrandomize' 85 86 private static String getCLV() { 87 String c = countries[r.nextInt(countries.length)]; 88 String l = languages[r.nextInt(languages.length)]; 89 String v = variants[r.nextInt(variants.length)]; 90 return new Locale(c, l, v).toString(); 91 } 92 93 private static boolean WAIT = true; 94 private static boolean GO = false; 95 private static long TIME = 5000; 96 97 public static void runThreads() { 98 runThreads(TIME); 99 } 100 101 public static void runThreads(long time) { 102 try { 103 GO = true; 104 WAIT = false; 105 106 Thread.sleep(time); 107 108 WAIT = true; 109 GO = false; 110 111 Thread.sleep(300); 112 } 113 catch (InterruptedException e) { 114 } 115 } 116 117 static class TestThread extends Thread { 118 //private final String name; 119 protected ICUService service; 120 private final long delay; 121 122 public TestThread(String name, ICUService service, long delay) { 123 //this.name = name + " "; 124 this.service = service; 125 this.delay = delay; 126 this.setDaemon(true); 127 } 128 129 @Override 130 public void run() { 131 while (WAIT) { 132 Thread.yield(); 133 } 134 135 try { 136 while (GO) { 137 iterate(); 138 if (delay > 0) { 139 Thread.sleep(delay); 140 } 141 } 142 } 143 catch (InterruptedException e) { 144 } 145 } 146 147 protected void iterate() { 148 } 149 150 /* 151 public boolean logging() { 152 return log != null; 153 } 154 155 public void log(String msg) { 156 if (logging()) { 157 log.log(name + msg); 158 } 159 } 160 161 public void logln(String msg) { 162 if (logging()) { 163 log.logln(name + msg); 164 } 165 } 166 167 public void err(String msg) { 168 if (logging()) { 169 log.err(name + msg); 170 } 171 } 172 173 public void errln(String msg) { 174 if (logging()) { 175 log.errln(name + msg); 176 } 177 } 178 179 public void warn(String msg) { 180 if (logging()) { 181 log.info(name + msg); 182 } 183 } 184 185 public void warnln(String msg) { 186 if (logging()) { 187 log.infoln(name + msg); 188 } 189 } 190 */ 191 } 192 193 static class RegisterFactoryThread extends TestThread { 194 RegisterFactoryThread(String name, ICUService service, long delay) { 195 super("REG " + name, service, delay); 196 } 197 198 @Override 199 protected void iterate() { 200 Factory f = new TestFactory(getCLV()); 201 service.registerFactory(f); 202 TestFmwk.logln(f.toString()); 203 } 204 } 205 206 static class UnregisterFactoryThread extends TestThread { 207 private Random r; 208 List factories; 209 210 UnregisterFactoryThread(String name, ICUService service, long delay) { 211 super("UNREG " + name, service, delay); 212 213 r = new Random(); 214 factories = service.factories(); 215 } 216 217 @Override 218 public void iterate() { 219 int s = factories.size(); 220 if (s == 0) { 221 factories = service.factories(); 222 } else { 223 int n = r.nextInt(s); 224 Factory f = (Factory)factories.remove(n); 225 boolean success = service.unregisterFactory(f); 226 TestFmwk.logln("factory: " + f + (success ? " succeeded." : " *** failed.")); 227 } 228 } 229 } 230 231 static class UnregisterFactoryListThread extends TestThread { 232 Factory[] factories; 233 int n; 234 235 UnregisterFactoryListThread(String name, ICUService service, long delay, Factory[] factories) { 236 super("UNREG " + name, service, delay); 237 238 this.factories = factories; 239 } 240 241 @Override 242 public void iterate() { 243 if (n < factories.length) { 244 Factory f = factories[n++]; 245 boolean success = service.unregisterFactory(f); 246 TestFmwk.logln("factory: " + f + (success ? " succeeded." : " *** failed.")); 247 } 248 } 249 } 250 251 252 static class GetVisibleThread extends TestThread { 253 GetVisibleThread(String name, ICUService service, long delay) { 254 super("VIS " + name, service, delay); 255 } 256 257 @Override 258 protected void iterate() { 259 Set ids = service.getVisibleIDs(); 260 Iterator iter = ids.iterator(); 261 int n = 10; 262 while (--n >= 0 && iter.hasNext()) { 263 String id = (String)iter.next(); 264 Object result = service.get(id); 265 TestFmwk.logln("iter: " + n + " id: " + id + " result: " + result); 266 } 267 } 268 } 269 270 static class GetDisplayThread extends TestThread { 271 ULocale locale; 272 273 GetDisplayThread(String name, ICUService service, long delay, ULocale locale) { 274 super("DIS " + name, service, delay); 275 276 this.locale = locale; 277 } 278 279 @Override 280 protected void iterate() { 281 Map names = getDisplayNames(service,locale); 282 Iterator iter = names.entrySet().iterator(); 283 int n = 10; 284 while (--n >= 0 && iter.hasNext()) { 285 Entry e = (Entry)iter.next(); 286 String dname = (String)e.getKey(); 287 String id = (String)e.getValue(); 288 Object result = service.get(id); 289 290 // Note: IllegalMonitorStateException is thrown by the code 291 // below on IBM JRE5 for AIX 64bit. For some reason, converting 292 // int to String out of this statement resolves the issue. 293 294 String num = Integer.toString(n); 295 TestFmwk.logln(" iter: " + num + 296 " dname: " + dname + 297 " id: " + id + 298 " result: " + result); 299 } 300 } 301 } 302 303 static class GetThread extends TestThread { 304 private String[] actualID; 305 306 GetThread(String name, ICUService service, long delay) { 307 super("GET " + name, service, delay); 308 309 actualID = new String[1]; 310 } 311 312 @Override 313 protected void iterate() { 314 String id = getCLV(); 315 Object o = service.get(id, actualID); 316 if (o != null) { 317 TestFmwk.logln(" id: " + id + " actual: " + actualID[0] + " result: " + o); 318 } 319 } 320 } 321 322 static class GetListThread extends TestThread { 323 private final String[] list; 324 private int n; 325 326 GetListThread(String name, ICUService service, long delay, String[] list) { 327 super("GETL " + name, service, delay); 328 329 this.list = list; 330 } 331 332 @Override 333 protected void iterate() { 334 if (--n < 0) { 335 n = list.length - 1; 336 } 337 String id = list[n]; 338 Object o = service.get(id); 339 TestFmwk.logln(" id: " + id + " result: " + o); 340 } 341 } 342 343 // return a collection of unique factories, might be fewer than requested 344 Collection getFactoryCollection(int requested) { 345 Set locales = new HashSet(); 346 for (int i = 0; i < requested; ++i) { 347 locales.add(getCLV()); 348 } 349 List factories = new ArrayList(locales.size()); 350 Iterator iter = locales.iterator(); 351 while (iter.hasNext()) { 352 factories.add(new TestFactory((String)iter.next())); 353 } 354 return factories; 355 } 356 357 void registerFactories(ICUService service, Collection c) { 358 Iterator iter = c.iterator(); 359 while (iter.hasNext()) { 360 service.registerFactory((Factory)iter.next()); 361 } 362 } 363 364 ICUService stableService() { 365 if (stableService == null) { 366 stableService = new ICULocaleService(); 367 registerFactories(stableService, getFactoryCollection(50)); 368 } 369 if (PRINTSTATS) stableService.stats(); // Enable the stats collection 370 return stableService; 371 } 372 private ICUService stableService; 373 374 // run multiple get on a stable service 375 @Test 376 public void Test00_ConcurrentGet() { 377 for(int i = 0; i < 10; ++i) { 378 new GetThread("[" + Integer.toString(i) + "]", stableService(), 0).start(); 379 } 380 runThreads(); 381 if (PRINTSTATS) System.out.println(stableService.stats()); 382 } 383 384 // run multiple getVisibleID on a stable service 385 @Test 386 public void Test01_ConcurrentGetVisible() { 387 for(int i = 0; i < 10; ++i) { 388 new GetVisibleThread("[" + Integer.toString(i) + "]", stableService(), 0).start(); 389 } 390 runThreads(); 391 if (PRINTSTATS) System.out.println(stableService.stats()); 392 } 393 394 // run multiple getDisplayName on a stable service 395 @Test 396 public void Test02_ConcurrentGetDisplay() { 397 String[] localeNames = { 398 "en", "es", "de", "fr", "zh", "it", "no", "sv" 399 }; 400 for(int i = 0; i < localeNames.length; ++i) { 401 String locale = localeNames[i]; 402 new GetDisplayThread("[" + locale + "]", 403 stableService(), 404 0, 405 new ULocale(locale)).start(); 406 } 407 runThreads(); 408 if (PRINTSTATS) System.out.println(stableService.stats()); 409 } 410 411 // run register/unregister on a service 412 @Test 413 public void Test03_ConcurrentRegUnreg() { 414 ICUService service = new ICULocaleService(); 415 if (PRINTSTATS) service.stats(); // Enable the stats collection 416 for (int i = 0; i < 5; ++i) { 417 new RegisterFactoryThread("[" + i + "]", service, 0).start(); 418 } 419 for (int i = 0; i < 5; ++i) { 420 new UnregisterFactoryThread("[" + i + "]", service, 0).start(); 421 } 422 runThreads(); 423 if (PRINTSTATS) System.out.println(service.stats()); 424 } 425 426 @Test 427 public void Test04_WitheringService() { 428 ICUService service = new ICULocaleService(); 429 if (PRINTSTATS) service.stats(); // Enable the stats collection 430 431 Collection fc = getFactoryCollection(50); 432 registerFactories(service, fc); 433 434 Factory[] factories = (Factory[])fc.toArray(new Factory[fc.size()]); 435 Comparator comp = new Comparator() { 436 @Override 437 public int compare(Object lhs, Object rhs) { 438 return lhs.toString().compareTo(rhs.toString()); 439 } 440 }; 441 Arrays.sort(factories, comp); 442 443 new GetThread("", service, 0).start(); 444 new UnregisterFactoryListThread("", service, 3, factories).start(); 445 446 runThreads(2000); 447 if (PRINTSTATS) System.out.println(service.stats()); 448 } 449 450 // "all hell breaks loose" 451 // one register and one unregister thread, delay 500ms 452 // two display threads with different locales, delay 500ms; 453 // one visible id thread, delay 50ms 454 // fifteen get threads, delay 0 455 // run for ten seconds 456 @Test 457 public void Test05_ConcurrentEverything() { 458 ICUService service = new ICULocaleService(); 459 if (PRINTSTATS) service.stats(); // Enable the stats collection 460 461 new RegisterFactoryThread("", service, 500).start(); 462 463 for(int i = 0; i < 15; ++i) { 464 new GetThread("[" + Integer.toString(i) + "]", service, 0).start(); 465 } 466 467 new GetVisibleThread("", service, 50).start(); 468 469 String[] localeNames = { 470 "en", "de" 471 }; 472 for(int i = 0; i < localeNames.length; ++i) { 473 String locale = localeNames[i]; 474 new GetDisplayThread("[" + locale + "]", 475 stableService(), 476 500, 477 new ULocale(locale)).start(); 478 } 479 480 new UnregisterFactoryThread("", service, 500).start(); 481 482 // yoweee!!! 483 runThreads(9500); 484 if (PRINTSTATS) System.out.println(service.stats()); 485 } 486 } 487