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