1 /* 2 * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package nsk.monitoring.share; 25 26 import java.lang.management.*; 27 import java.util.*; 28 29 import nsk.share.*; 30 31 /** 32 * The <code>ThreadController</code> class allows to operate with threads. 33 */ 34 public class ThreadController extends StateController { 35 36 /** 37 * Type of threads: pure java. 38 */ 39 static public final int JAVA_TYPE = 0; 40 41 /** 42 * Type of threads: native. 43 */ 44 static public final int NATIVE_TYPE = 1; 45 46 /** 47 * Type of threads: both java and native. 48 */ 49 static public final int MIXED_TYPE = 2; 50 51 /** 52 * Result code: no errors. 53 */ 54 static public final int NO_ERROR = 0; 55 56 /** 57 * Result code: wrong state of the thread. 58 */ 59 static public final int ERR_STATE = 1; 60 61 /** 62 * Result code: error in stack trace. 63 */ 64 static public final int ERR_STACKTRACE = 2; 65 66 /** 67 * Result code: thread not found. 68 */ 69 static public final int ERR_THREAD_NOTFOUND = 3; 70 71 72 // Prefix to print while logging 73 static final String LOG_PREFIX = "ThreadController> "; 74 75 // Internal trace levels 76 static final int THREAD_TRACE_LEVEL = 50; 77 78 /** 79 * Suffix of all started threads. 80 */ 81 static final String THREAD_SUFFIX = "_ThreadMM"; 82 83 // Number of tested kinds of threads 84 public static final int THREAD_KIND_COUNT = 4; 85 86 /** 87 * Index of blocked threads. 88 */ 89 static public final int BLOCKED_IDX = 0; 90 91 /** 92 * Index of waiting threads. 93 */ 94 static public final int WAITING_IDX = 1; 95 96 /** 97 * Index of sleeping threads. 98 */ 99 static public final int SLEEPING_IDX = 2; 100 101 /** 102 * Index of running threads. 103 */ 104 static public final int RUNNING_IDX = 3; 105 106 public static final String[] THREAD_KIND_NAMES = {"BLOCKED","WAITING","SLEEPING","RUNNABLE"}; 107 public static final Thread.State[] THREAD_KINDS = {Thread.State.BLOCKED, Thread.State.WAITING, Thread.State.TIMED_WAITING, Thread.State.RUNNABLE}; 108 109 private Map<Thread.State, Integer> threadsCount = new HashMap<Thread.State, Integer>(); 110 private Map<Thread.State, List<BaseThread>> threadsClusters = new HashMap<Thread.State, List<BaseThread>>(); 111 112 private ThreadsGroupLocks threadsGroupLocks; 113 114 115 int maxDepth; 116 static int invocationType; 117 118 static { 119 try { 120 System.loadLibrary("ThreadController"); 121 } catch (UnsatisfiedLinkError e) { 122 System.err.println("Could not load \"ThreadController\" " 123 + "library"); 124 System.err.println("java.library.path:" 125 + System.getProperty("java.library.path")); 126 throw e; 127 } 128 } 129 130 /** 131 * Creates a new <code>ThreadController</code> object with defined 132 * arguments.. 133 * 134 * @param log <code>Log</code> object to print info to. 135 * @param threadCount number of threads to start. 136 * @param maxDepth depth of recursion. 137 * @param invocationType type of threads to start (java, native, or mixed). 138 */ ThreadController(Log log, int threadCount, int maxDepth, String invocationType)139 public ThreadController(Log log, int threadCount, int maxDepth, 140 String invocationType) { 141 logPrefix = LOG_PREFIX; 142 setLog(log); 143 setThreadCount(threadCount); 144 setDepth(maxDepth); 145 setInvocationType(invocationType); 146 } 147 148 149 // Calculate how many threads of each kind to start setThreadCount(int threadCount)150 private void setThreadCount(int threadCount) { 151 int total = 0; 152 int kinds = THREAD_KIND_COUNT; 153 int tmp = threadCount / kinds; 154 int rest = threadCount % kinds; 155 int increased = kinds - rest; 156 for (int i = 0; i < kinds; i++) { 157 if (i >= increased) { 158 threadsCount.put(THREAD_KINDS[i], tmp + 1); 159 } else { 160 threadsCount.put(THREAD_KINDS[i], tmp); 161 } 162 } 163 display("number of created threads:\t" + threadCount); 164 } 165 166 // Print thread count printThreadCount()167 private void printThreadCount() { 168 for (Thread.State state : THREAD_KINDS) { 169 display("\t" + state + " threads (" 170 + threadsCount.get(state) + ")"); 171 } 172 } 173 174 // Set recursion depth setDepth(int depth)175 private void setDepth(int depth) { 176 maxDepth = depth; 177 display("depth for all threads:\t" + maxDepth); 178 } 179 180 // Set invocation type setInvocationType(String value)181 private void setInvocationType(String value) { 182 display("invocation type:\t" + value); 183 if (value.equals(ArgumentHandler.JAVA_TYPE)) { 184 invocationType = JAVA_TYPE; 185 } else if (value.equals(ArgumentHandler.NATIVE_TYPE)) { 186 invocationType = NATIVE_TYPE; 187 } else if (value.equals(ArgumentHandler.MIXED_TYPE)) { 188 invocationType = MIXED_TYPE; 189 } else { 190 throw new Failure("UNKNOWN invocation type"); 191 } 192 } 193 194 /** 195 * Returns invocation type. 196 * 197 * @return invocation type. 198 */ getInvocationType()199 public int getInvocationType() { 200 return invocationType; 201 } 202 203 /** 204 * Returns thread count. 205 * 206 * @param state kind of thread state 207 * @return thread count. 208 */ getThreadCount(Thread.State state)209 public int getThreadCount(Thread.State state) { 210 return threadsCount.get(state); 211 } 212 213 /** 214 * Returns thread count. 215 * 216 * @param kindIndex of thread state 217 * @return thread count. 218 */ getThreadCount(int kindIndex)219 public int getThreadCount(int kindIndex) { 220 return threadsCount.get(THREAD_KINDS[kindIndex]); 221 } 222 getThreadKindCount()223 public int getThreadKindCount() { 224 return THREAD_KINDS.length; 225 } 226 227 228 /** 229 * Brings out VM into defined state. 230 * <p/> 231 * The method starts all threads. 232 */ run()233 public void run() { 234 long startTime = System.currentTimeMillis() / 1000; 235 startThreads(); 236 display("locking threads"); 237 waitForThreads(); 238 } 239 240 /** 241 * Tries to return VM into initial state 242 * <p/> 243 * The method interrupts all threads. 244 */ reset()245 public void reset() { 246 for (Thread.State state : THREAD_KINDS) { 247 threadsGroupLocks.releaseGroup(state); 248 } 249 } 250 251 // Get thread state via JVMTI getThreadState(Thread thread)252 private native Thread.State getThreadState(Thread thread); 253 254 // Start all threads startThreads()255 private void startThreads() throws Failure { 256 257 String tmp_name; 258 BaseThread thread = null; 259 260 threadsGroupLocks = new ThreadsGroupLocks(threadsCount, logger); 261 for (Thread.State state : THREAD_KINDS) { 262 threadsClusters.put(state, new ArrayList<BaseThread>()); 263 for (int j = 0; j < threadsCount.get(state); j++) { 264 tmp_name = state + THREAD_SUFFIX + int2Str(j); 265 switch (state) { 266 case BLOCKED: 267 thread = new BlockedThread(this, tmp_name, logger.getLog(), threadsGroupLocks); 268 break; 269 case WAITING: 270 thread = new WaitingThread(this, tmp_name, logger.getLog(), threadsGroupLocks); 271 break; 272 case TIMED_WAITING: 273 thread = new SleepingThread(this, tmp_name, logger.getLog(), threadsGroupLocks); 274 break; 275 case RUNNABLE: 276 thread = new RunningThread(this, tmp_name, logger.getLog(), threadsGroupLocks); 277 break; 278 default: 279 throw new TestBug("Unknow thread kind"); 280 } 281 threadsClusters.get(state).add(thread); 282 thread.start(); 283 } 284 } 285 waitForThreads(); 286 } 287 checkState(Thread.State expectedState)288 private boolean checkState(Thread.State expectedState) { 289 for (Thread thread : threadsClusters.get(expectedState)) { 290 if (getThreadState(thread) != expectedState) { 291 292 return false; 293 } 294 } 295 return true; 296 } 297 waitForThreads()298 private void waitForThreads() { 299 for (Thread.State state : THREAD_KINDS) { 300 threadsGroupLocks.waitForGroup(state); 301 while (!checkState(state)) { 302 Thread.yield(); 303 } 304 } 305 } 306 307 308 /** 309 * Finds a thread with defined id. 310 * 311 * @param id ID of the thread. 312 * @return a thread with defined id. 313 */ findThread(long id)314 public BaseThread findThread(long id) { 315 for(Thread.State state:THREAD_KINDS){ 316 for(BaseThread thread:threadsClusters.get(state)){ 317 if (id==thread.getId()) { 318 return thread; 319 } 320 } 321 } 322 return null; 323 } 324 325 /** 326 * Finds a thread by name. 327 * 328 * @param name name of the thread. 329 * @return a thread with defined name. 330 */ findThread(String name)331 public BaseThread findThread(String name) { 332 for(Thread.State state:THREAD_KINDS){ 333 for(BaseThread thread:threadsClusters.get(state)){ 334 if (name.equals(thread.getName())) { 335 return thread; 336 } 337 } 338 } 339 return null; 340 } 341 342 /** 343 * Checks the thread's <code>ThreadInfo</code>. 344 * 345 * @param info <code>ThreadInfo</code> object to test. 346 * @return result code. 347 * @see #NO_ERROR 348 * @see #ERR_THREAD_NOTFOUND 349 * @see #ERR_STATE 350 * @see #ERR_STACKTRACE 351 */ checkThreadInfo(ThreadInfo info)352 public int checkThreadInfo(ThreadInfo info) { 353 String name = info.getThreadName(); 354 355 if (name.indexOf(THREAD_SUFFIX) == -1) { 356 return NO_ERROR; 357 } 358 359 long id = info.getThreadId(); 360 Thread.State state = info.getThreadState(); 361 StackTraceElement[] stackTrace = info.getStackTrace(); 362 363 BaseThread thrd = findThread(id); 364 if (thrd == null) { 365 return ERR_THREAD_NOTFOUND; 366 } 367 368 if (!thrd.checkState(state)) 369 return ERR_STATE; 370 371 if (!thrd.checkStackTrace(stackTrace)) 372 return ERR_STACKTRACE; 373 374 return NO_ERROR; 375 } 376 } 377 378 abstract class BaseThread extends Thread { 379 380 private int currentDepth = 0; 381 private String logPrefix; 382 protected Log.Logger logger; 383 384 protected ThreadController controller; 385 386 protected List<String> expectedMethods = new ArrayList<String>(); 387 protected int expectedLength; 388 389 protected ThreadsGroupLocks threadsGroupLocks; 390 391 static { 392 if (ThreadController.invocationType == ThreadController.NATIVE_TYPE || 393 ThreadController.invocationType == ThreadController.MIXED_TYPE) { 394 try { 395 System.loadLibrary("ThreadController"); 396 } catch (UnsatisfiedLinkError e) { 397 System.err.println("Could not load \"ThreadController\" " 398 + "library"); 399 System.err.println("java.library.path:" 400 + System.getProperty("java.library.path")); 401 throw e; 402 } 403 } 404 } 405 BaseThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks)406 public BaseThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) { 407 super(name); 408 this.controller = controller; 409 int pos = controller.LOG_PREFIX.indexOf('>'); 410 logPrefix = controller.LOG_PREFIX.substring(0, pos) + "::" 411 + name + "> "; 412 setLog(log); 413 this.threadsGroupLocks = threadsGroupLocks; 414 415 expectedLength = 1 + controller.maxDepth + 1; 416 if(controller.invocationType == ThreadController.MIXED_TYPE) { 417 //nativeRecursiveMethod 418 expectedLength ++; 419 } 420 421 expectedMethods.add(BaseThread.class.getName() + ".run"); 422 423 switch (controller.invocationType) { 424 case ThreadController.JAVA_TYPE: 425 expectedMethods.add(BaseThread.class.getName() + ".recursiveMethod"); 426 break; 427 case ThreadController.NATIVE_TYPE: 428 expectedMethods.add(BaseThread.class.getName() + ".nativeRecursiveMethod"); 429 break; 430 case ThreadController.MIXED_TYPE: 431 expectedMethods.add(BaseThread.class.getName() + ".recursiveMethod"); 432 expectedMethods.add(BaseThread.class.getName() + ".nativeRecursiveMethod"); 433 } 434 435 expectedMethods.add(ThreadsGroupLocks.PlainCountDownLatch.class.getName() + ".countDown"); 436 } 437 run()438 public void run() { 439 try { 440 switch (controller.invocationType) { 441 case ThreadController.JAVA_TYPE: 442 case ThreadController.MIXED_TYPE: 443 recursiveMethod(); 444 break; 445 case ThreadController.NATIVE_TYPE: 446 nativeRecursiveMethod(); 447 break; 448 default: 449 throw new Failure("unknown invocationType:" 450 + controller.invocationType); 451 } 452 } catch (StackOverflowError e) { 453 logger.complain(e.toString()); 454 throw new RuntimeException(e); 455 } 456 logger.trace(controller.THREAD_TRACE_LEVEL, "thread finished"); 457 } 458 bringState()459 protected abstract void bringState(); 460 getState()461 public abstract State getState(); 462 nativeBringState()463 protected abstract void nativeBringState(); 464 checkState(Thread.State state)465 public abstract boolean checkState(Thread.State state); 466 checkStackTrace(StackTraceElement[] elements)467 public boolean checkStackTrace(StackTraceElement[] elements) { 468 boolean res = true; 469 470 logger.trace(controller.THREAD_TRACE_LEVEL, "trace elements: " 471 + elements.length); 472 473 if (elements.length > expectedLength) { 474 res = false; 475 logger.complain("Contains " + elements.length + ", more then " 476 + expectedLength + " elements"); 477 } 478 479 for (int j = 0; j < elements.length; j++) { 480 if (!checkElement(elements[j])) { 481 logger.complain("Unexpected method name: " 482 + elements[j].getMethodName() 483 + " at " + j + " position"); 484 if (elements[j].isNativeMethod()) { 485 logger.complain("\tline number: (native method)"); 486 logger.complain("\tclass name: " + elements[j].getClassName()); 487 } else { 488 logger.complain("\tline number: " + elements[j].getLineNumber()); 489 logger.complain("\tclass name: " + elements[j].getClassName()); 490 logger.complain("\tfile name: " + elements[j].getFileName()); 491 } 492 res = false; 493 } 494 } 495 return res; 496 } 497 checkElement(StackTraceElement element)498 protected boolean checkElement(StackTraceElement element) { 499 String name = element.getClassName() + "." + element.getMethodName(); 500 if (expectedMethods.contains(name)) { 501 return true; 502 } 503 504 logger.trace(controller.THREAD_TRACE_LEVEL, "\"" + name + "\"" 505 + " is not expected method name"); 506 return false; 507 } 508 recursiveMethod()509 protected void recursiveMethod() { 510 currentDepth++; 511 512 if (controller.maxDepth - currentDepth > 0) { 513 514 Thread.yield(); 515 try { 516 if (ThreadController.invocationType 517 == ThreadController.MIXED_TYPE) { 518 nativeRecursiveMethod(); 519 } else { 520 recursiveMethod(); 521 } 522 523 } catch (StackOverflowError e) { 524 logger.display(getName() + "> " + e); 525 } 526 527 } else if (controller.maxDepth == currentDepth) { 528 logger.trace(controller.THREAD_TRACE_LEVEL, "state has been " 529 + "reached"); 530 bringState(); 531 } 532 currentDepth--; 533 } 534 nativeRecursiveMethod()535 protected native void nativeRecursiveMethod(); 536 537 /** 538 * Defines <code>Log.Logger</code> object 539 */ setLog(Log log)540 public void setLog(Log log) { 541 logger = new Log.Logger(log, logPrefix); 542 } 543 } 544 545 class BlockedThread extends BaseThread { 546 547 private static final Thread.State STATE = Thread.State.BLOCKED; 548 getState()549 public State getState() { 550 return STATE; 551 } 552 BlockedThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks)553 public BlockedThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) { 554 super(controller, name, log, threadsGroupLocks); 555 556 this.threadsGroupLocks = threadsGroupLocks; 557 558 expectedLength += 2; 559 560 expectedMethods.add(ThreadsGroupLocks.Blocker.class.getName() + ".block"); 561 562 switch (controller.invocationType) { 563 case ThreadController.JAVA_TYPE: 564 expectedMethods.add(BlockedThread.class.getName() + ".bringState"); 565 break; 566 case ThreadController.NATIVE_TYPE: 567 expectedMethods.add(BlockedThread.class.getName() + ".nativeBringState"); 568 break; 569 case ThreadController.MIXED_TYPE: 570 expectedMethods.add(BlockedThread.class.getName() + ".bringState"); 571 572 } 573 } 574 bringState()575 protected void bringState() { 576 logger.trace(controller.THREAD_TRACE_LEVEL, "entering to monitor"); 577 threadsGroupLocks.getBarrier(getState()).countDown(); 578 threadsGroupLocks.blocker.block(); 579 logger.trace(controller.THREAD_TRACE_LEVEL, "exiting from monitor"); 580 } 581 nativeBringState()582 protected native void nativeBringState(); 583 checkState(Thread.State state)584 public boolean checkState(Thread.State state) { 585 return state == Thread.State.BLOCKED; 586 } 587 } 588 589 class WaitingThread extends BaseThread { 590 591 private static final Thread.State STATE = Thread.State.WAITING; getState()592 public State getState() { 593 return STATE; 594 } 595 596 private ThreadsGroupLocks threadsGroupLocks; 597 WaitingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks)598 public WaitingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) { 599 super(controller, name, log, threadsGroupLocks); 600 601 this.threadsGroupLocks = threadsGroupLocks; 602 603 expectedLength += 4; 604 605 expectedMethods.add(ThreadsGroupLocks.PlainCountDownLatch.class.getName() + ".await"); 606 expectedMethods.add(Object.class.getName() + ".wait"); 607 608 switch (controller.invocationType) { 609 case ThreadController.JAVA_TYPE: 610 expectedMethods.add(WaitingThread.class.getName() + ".bringState"); 611 break; 612 case ThreadController.NATIVE_TYPE: 613 expectedMethods.add(WaitingThread.class.getName() + ".nativeBringState"); 614 break; 615 case ThreadController.MIXED_TYPE: 616 expectedMethods.add(WaitingThread.class.getName() + ".bringState"); 617 618 } 619 } 620 621 bringState()622 protected void bringState() { 623 ThreadsGroupLocks.PlainCountDownLatch barrier = threadsGroupLocks.getBarrier(STATE); 624 try { 625 logger.trace(controller.THREAD_TRACE_LEVEL, "waiting on a monitor"); 626 threadsGroupLocks.getBarrier(getState()).countDown(); 627 barrier.await(); 628 } catch (InterruptedException e) { 629 logger.display(e.toString()); 630 } 631 } 632 nativeBringState()633 protected native void nativeBringState(); 634 checkState(Thread.State state)635 public boolean checkState(Thread.State state) { 636 return state == STATE; 637 } 638 639 } 640 641 class SleepingThread extends BaseThread { 642 private static final Thread.State STATE = State.TIMED_WAITING; 643 getState()644 public State getState() { 645 return STATE; 646 } 647 648 private ThreadsGroupLocks threadsGroupLocks; 649 SleepingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks)650 public SleepingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) { 651 super(controller, name, log, threadsGroupLocks); 652 653 this.threadsGroupLocks = threadsGroupLocks; 654 655 expectedLength += 3; 656 657 expectedMethods.add(Thread.class.getName() + ".sleep"); 658 expectedMethods.add(SleepingThread.class.getName() + ".run"); 659 660 switch (controller.invocationType) { 661 case ThreadController.JAVA_TYPE: 662 expectedMethods.add(SleepingThread.class.getName() + ".bringState"); 663 break; 664 case ThreadController.NATIVE_TYPE: 665 expectedMethods.add(SleepingThread.class.getName() + ".nativeBringState"); 666 break; 667 case ThreadController.MIXED_TYPE: 668 expectedMethods.add(SleepingThread.class.getName() + ".bringState"); 669 } 670 671 } 672 bringState()673 protected void bringState() { 674 try { 675 threadsGroupLocks.getBarrier(getState()).countDown(); 676 Thread.sleep(3600 * 1000); 677 } catch (InterruptedException e) { 678 logger.display(e.toString()); 679 } 680 } 681 nativeBringState()682 protected native void nativeBringState(); 683 checkState(Thread.State state)684 public boolean checkState(Thread.State state) { 685 return state == Thread.State.TIMED_WAITING; 686 } 687 run()688 public void run() { 689 try { 690 switch (controller.invocationType) { 691 case ThreadController.JAVA_TYPE: 692 case ThreadController.MIXED_TYPE: 693 recursiveMethod(); 694 break; 695 case ThreadController.NATIVE_TYPE: 696 nativeRecursiveMethod(); 697 break; 698 default: 699 throw new Failure("unknown invocationType:" 700 + controller.invocationType); 701 } 702 logger.trace(controller.THREAD_TRACE_LEVEL, "thread finished"); 703 } catch (StackOverflowError e) { 704 logger.complain(e.toString()); 705 throw new RuntimeException(e); 706 } 707 } 708 } 709 710 class RunningThread extends BaseThread { getState()711 public State getState() { 712 return STATE; 713 } 714 715 private static final Thread.State STATE = Thread.State.RUNNABLE; 716 private ThreadsGroupLocks threadsGroupLocks; 717 RunningThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks)718 public RunningThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) { 719 super(controller, name, log, threadsGroupLocks); 720 this.threadsGroupLocks = threadsGroupLocks; 721 722 expectedLength += 2; 723 724 expectedMethods.add(Thread.class.getName() + ".yield"); 725 726 switch (controller.invocationType) { 727 case ThreadController.JAVA_TYPE: 728 expectedMethods.add(RunningThread.class.getName() + ".bringState"); 729 break; 730 case ThreadController.NATIVE_TYPE: 731 expectedMethods.add(RunningThread.class.getName() + ".nativeBringState"); 732 break; 733 case ThreadController.MIXED_TYPE: 734 expectedMethods.add(RunningThread.class.getName() + ".bringState"); 735 } 736 } 737 bringState()738 protected void bringState() { 739 logger.trace(controller.THREAD_TRACE_LEVEL, "running loop"); 740 threadsGroupLocks.getBarrier(getState()).countDown(); 741 while (!threadsGroupLocks.runnableCanExit) { 742 Thread.yield(); 743 } 744 } 745 nativeBringState()746 protected native void nativeBringState(); 747 checkState(Thread.State state)748 public boolean checkState(Thread.State state) { 749 return state == Thread.State.RUNNABLE; 750 } 751 } 752 753 754 class ThreadsGroupLocks { 755 756 private Log.Logger logger; 757 758 //for all 759 private Map<Thread.State, PlainCountDownLatch> barriers = new HashMap<Thread.State, PlainCountDownLatch>(); 760 761 //for Blocked 762 public final Blocker blocker = new Blocker(); 763 764 //for Runnable 765 public volatile boolean runnableCanExit = false; 766 ThreadsGroupLocks(Map<Thread.State, Integer> threadsCount, Log.Logger logger)767 public ThreadsGroupLocks(Map<Thread.State, Integer> threadsCount, Log.Logger logger) { 768 this.logger = logger; 769 for (Thread.State state : threadsCount.keySet()) { 770 if (state == Thread.State.WAITING) { 771 barriers.put(state, new PlainCountDownLatch(threadsCount.get(state) + 1)); 772 } else { 773 barriers.put(state, new PlainCountDownLatch(threadsCount.get(state))); 774 } 775 } 776 blocker.startBlocker(); 777 } 778 getBarrier(Thread.State state)779 public PlainCountDownLatch getBarrier(Thread.State state) { 780 return barriers.get(state); 781 } 782 waitForGroup(Thread.State stateGroup)783 public void waitForGroup(Thread.State stateGroup) { 784 switch (stateGroup) { 785 case BLOCKED: 786 case RUNNABLE: 787 case TIMED_WAITING: 788 try { 789 barriers.get(stateGroup).await(); 790 } catch (InterruptedException e) { 791 logger.display(e.toString()); 792 } 793 break; 794 795 case WAITING: 796 while (barriers.get(stateGroup).getCount() != 1) { 797 Thread.yield(); 798 } 799 break; 800 } 801 } 802 releaseGroup(Thread.State stateGroup)803 public void releaseGroup(Thread.State stateGroup) { 804 switch (stateGroup) { 805 case BLOCKED: 806 blocker.unBlock(); 807 break; 808 case RUNNABLE: 809 runnableCanExit = true; 810 break; 811 case TIMED_WAITING: 812 case WAITING: 813 barriers.get(stateGroup).countDown(); 814 break; 815 } 816 } 817 818 public class Blocker { 819 820 private Object monitor = new Object(); 821 private PlainCountDownLatch blockerCanExit = new PlainCountDownLatch(1); 822 private PlainCountDownLatch blockerStart = new PlainCountDownLatch(1); 823 824 private Runnable blockerThread = new Runnable() { 825 public void run() { 826 synchronized (monitor) { 827 blockerStart.countDown(); 828 try { 829 blockerCanExit.await(); 830 } catch (InterruptedException e) { 831 logger.display(e.toString()); 832 } 833 834 } 835 } 836 }; 837 startBlocker()838 public void startBlocker() { 839 new Thread(blockerThread, "Blocker").start(); 840 } 841 block()842 public void block() { 843 try { 844 blockerStart.await(); 845 } catch (InterruptedException e) { 846 logger.complain(e.toString()); 847 } 848 synchronized (monitor) { 849 } 850 } 851 unBlock()852 public void unBlock() { 853 blockerCanExit.countDown(); 854 } 855 } 856 857 public static class PlainCountDownLatch { 858 private volatile int counter; 859 private Object counterMonitor = new Object(); 860 PlainCountDownLatch(int counter)861 public PlainCountDownLatch(int counter){ 862 this.counter = counter; 863 } 864 countDown()865 public void countDown(){ 866 synchronized (counterMonitor) { 867 counter--; 868 if(counter==0) { 869 counterMonitor.notifyAll(); 870 } 871 } 872 } 873 await()874 public void await() throws InterruptedException{ 875 synchronized (counterMonitor){ 876 while(counter != 0){ 877 counterMonitor.wait(); 878 } 879 } 880 } 881 getCount()882 public int getCount(){ 883 synchronized (counterMonitor) { 884 return counter; 885 } 886 } 887 } 888 889 } 890