2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 import java.io.PrintStream;
24 * our model of java.lang.ThreadGroup, which we need since the VM relies on certain fields
25 * during bootstrapping
27 * Note that we don't have a native peer, which would help with atomic container updates, because
28 * most of these methods are called by the VM and we don't want to introduce a dependency from
29 * VM to the peers. This is esp. true since we would have to dig out & cast the peer, call it,
30 * and then go backwards extracting the *Info objects we already had in the calling context.
32 * If we ever want to introduce a peer, it could just delegate to the respective ThreadInfo methods.
34 public class ThreadGroup implements Thread.UncaughtExceptionHandler {
35 private final ThreadGroup parent;
37 //--- those are package visible, so we better keep names and access modifiers
48 int nUnstartedThreads;
53 boolean vmAllowSuspension;
55 public ThreadGroup (String name){
56 this( Thread.currentThread().getThreadGroup(), name);
59 public ThreadGroup (ThreadGroup parent, String name){
63 // those are inherited from the parent, which therefore can't be null
64 this.maxPriority = parent.maxPriority;
65 this.daemon = parent.daemon;
70 //--- the container updates
72 // group updates are just class internal, whereas thread updates happen from java.lang.Thread and hence need
75 private synchronized void add (ThreadGroup childGroup){
77 throw new IllegalThreadStateException();
81 groups = new ThreadGroup[1];
83 ThreadGroup[] ng = new ThreadGroup[ngroups+1];
84 System.arraycopy(groups, 0, ng, 0, ngroups);
88 groups[ngroups++] = childGroup;
91 private synchronized void remove (ThreadGroup childGroup){
93 for (int i=0; i<ngroups; i++){
94 if (groups[i] == childGroup){
96 int ngroups1 = ngroups - 1;
97 ThreadGroup[] ng = new ThreadGroup[ngroups1];
99 System.arraycopy(groups, 0, ng, 0, i);
102 System.arraycopy(groups, i + 1, ng, i, ngroups - i);
116 // check if its time for a suicide in case we are a daemon group
118 if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)){
130 // thread add/remove happens from the native peer, to avoid additional CGs during creation/termination
131 // since the ThreadGroup is per se a shared object. We just include these methods here as a specification
135 * called from the Thread ctor, i.e. before it is started
136 * NOTE - this is implemented as a native method to avoid shared field CGs
138 synchronized void addUnstarted (){
140 throw new IllegalThreadStateException();
147 * this is called during Thread.start()
148 * NOTE - this is implemented as a native method to avoid shared field CGs
150 synchronized void add (Thread newThread){
152 throw new IllegalThreadStateException();
155 if (threads == null){
156 threads = new Thread[1];
158 Thread[] nt = new Thread[nthreads+1];
159 System.arraycopy(threads, 0, nt, 0, nthreads);
163 threads[nthreads++] = newThread;
168 * this is called automatically upon thread termination
170 synchronized void threadTerminated (Thread terminatedThread){
172 for (int i=0; i<nthreads; i++){
173 if (threads[i] == terminatedThread){
179 int nthreads1 = nthreads - 1;
180 Thread[] a = new Thread[nthreads1];
182 System.arraycopy(threads, 0, a, 0, i);
185 System.arraycopy(groups, i + 1, a, i, ngroups - i);
188 nthreads = nthreads1;
191 // OpenJDK does this no matter if the group was destroyed, but that looks like a bug
192 // that could lead to multiple destroys and notifyAll() signals
197 if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) {
208 //--- public getters and setters
209 public final String getName(){
213 public final ThreadGroup getParent(){
217 public final int getMaxPriority(){
221 public final boolean isDaemon(){
225 public synchronized boolean isDestroyed(){
229 public final void setDaemon (boolean daemon){
230 this.daemon = daemon;
234 * this sets the maxPriority to the min of the argument and the parents maxPriority
235 * and raises maxPriority for all our threads that have a lower one
237 public final synchronized void setMaxPriority (int newMaxPriority){
238 // do nothing if newMaxPriority is outside limits
239 if (newMaxPriority >= Thread.MIN_PRIORITY && newMaxPriority <= Thread.MAX_PRIORITY){
240 maxPriority = (parent != null) ? Math.min(parent.maxPriority, newMaxPriority) : newMaxPriority;
242 for (int i=0; i<groups.length; i++){
243 groups[i].setMaxPriority(newMaxPriority);
248 public final boolean parentOf (ThreadGroup tg){
250 if (tg == this) return true;
251 ThreadGroup tgParent = tg.parent;
252 if (tgParent == null) return false;
257 public final void checkAccess(){
258 // not sure what to do here
262 * return number of threads including all sub-groups
264 public synchronized int activeCount() {
269 int nActive = nthreads;
271 for (int i=0; i<ngroups; i++){
272 nActive += groups[i].activeCount();
281 * copy threads into provided list
284 public int enumerate (Thread[] dst){
285 return enumerate(dst, 0, true);
288 public int enumerate (Thread[] dst, boolean recurse){
289 return enumerate(dst, 0, recurse);
292 private synchronized int enumerate (Thread[] dst, int idx, boolean recurse){
296 if ((idx + len) > dst.length){
297 len = dst.length - idx;
300 for (int i = 0; i < len; i++) {
301 if (threads[i].isAlive()) {
302 dst[idx++] = threads[i];
307 if (recurse && (idx < dst.length)) {
308 for (int j = 0; j < ngroups; j++) {
309 n += groups[j].enumerate(dst, idx, true);
316 public synchronized int activeGroupCount() {
321 int nActive = ngroups;
323 for (int i=0; i<ngroups; i++){
324 nActive += groups[i].activeGroupCount();
332 public int enumerate (ThreadGroup[] dst){
333 return enumerate(dst, 0, true);
336 public int enumerate (ThreadGroup[] dst, boolean recurse){
337 return enumerate(dst, 0, recurse);
340 private synchronized int enumerate (ThreadGroup[] dst, int idx, boolean recurse){
344 if ((idx + len) > dst.length){
345 len = dst.length - idx;
348 for (int i = 0; i < len; i++) {
349 dst[idx++] = groups[i];
353 if (recurse && (idx < dst.length)) {
354 for (int j = 0; j < ngroups; j++) {
355 n += groups[j].enumerate(dst, idx, true);
362 static final int OP_STOP = 1;
363 static final int OP_INTERRUPT = 2;
364 static final int OP_SUSPEND = 3;
365 static final int OP_RESUME = 4;
367 public final void stop(){
368 if (doRecursively(OP_STOP)){
369 Thread.currentThread().stop();
372 public final void interrupt(){
373 doRecursively(OP_INTERRUPT);
375 public final void suspend(){
376 if (doRecursively(OP_SUSPEND)){
377 Thread.currentThread().suspend();
380 public final void resume(){
381 doRecursively(OP_RESUME);
384 private synchronized boolean doRecursively (int op){
385 boolean suicide = false;
387 for (int i=0; i<nthreads; i++){
388 Thread t = threads[i];
391 if (t == Thread.currentThread()) {
392 suicide = true; // defer
397 case OP_INTERRUPT: t.interrupt(); break;
399 if (t == Thread.currentThread()) {
400 suicide = true; // defer
405 case OP_RESUME: t.resume(); break;
409 for (int j=0; j<ngroups; j++){
410 suicide = suicide || groups[j].doRecursively(op);
417 public synchronized final void destroy(){
418 if (destroyed || (nthreads > 0)) {
419 throw new IllegalThreadStateException();
422 for (int i=0; i<ngroups; i++){
426 if (parent != null){ // no destroying the system group
439 //--- just for debugging
441 list( System.out, 0);
443 synchronized void list (PrintStream ps, int indent){
444 for (int i=0; i<indent; i++) ps.print(' ');
445 ps.println(toString());
448 for (int j=0; j<nthreads; j++){
449 for (int i=0; i<indent; i++) ps.print(' ');
450 ps.println( threads[j]);
453 for (int k=0; k<ngroups; k++){
454 groups[k].list( ps, indent);
458 public boolean allowThreadSuspension (boolean allowSuspension){
459 vmAllowSuspension = allowSuspension;
464 public String toString () {
465 return getClass().getName() + "[name=" + name + ",maxpri=" + maxPriority + ']';
468 // we handle this in ThreadInfo, but again this is a public method that could be called from anywhere
470 public void uncaughtException (Thread t, Throwable x) {
471 if (parent != null) { // if we have a parent, delegate
472 parent.uncaughtException(t, x);
474 } else { // check if there is a default handler
475 Thread.UncaughtExceptionHandler xh = Thread.getDefaultUncaughtExceptionHandler();
477 xh.uncaughtException(t, x);
479 } else { // last resort - just print to Ssytem.err
480 if (!(x instanceof ThreadDeath)) {
481 System.err.print("Exception in thread \"" + t.getName() + '"');
482 x.printStackTrace(System.err);