Initial import
[jpf-core.git] / src / classes / java / lang / ThreadGroup.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
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
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
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.
17  */
18
19 package java.lang;
20
21 import java.io.PrintStream;
22
23 /**
24  * our model of java.lang.ThreadGroup, which we need since the VM relies on certain fields
25  * during bootstrapping
26  * 
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.
31  * 
32  * If we ever want to introduce a peer, it could just delegate to the respective ThreadInfo methods.
33  */
34 public class ThreadGroup implements Thread.UncaughtExceptionHandler {
35   private final ThreadGroup parent;
36   
37   //--- those are package visible, so we better keep names and access modifiers
38   
39   String name;
40   int maxPriority;
41  
42   int nthreads;
43   Thread[] threads;
44   
45   int ngroups;
46   ThreadGroup[] groups;
47   
48   int nUnstartedThreads;
49   
50   boolean destroyed;
51   boolean daemon;
52   
53   boolean vmAllowSuspension;
54   
55   public ThreadGroup (String name){
56     this( Thread.currentThread().getThreadGroup(), name);
57   }
58   
59   public ThreadGroup (ThreadGroup parent, String name){
60     this.parent = parent;
61     this.name = name;
62     
63     // those are inherited from the parent, which therefore can't be null
64     this.maxPriority = parent.maxPriority;
65     this.daemon = parent.daemon;
66     
67     parent.add(this);
68   }
69   
70   //--- the container updates
71   
72   // group updates are just class internal, whereas thread updates happen from java.lang.Thread and hence need
73   // package visibility
74   
75   private synchronized void add (ThreadGroup childGroup){
76     if (destroyed){
77       throw new IllegalThreadStateException();
78     }
79     
80     if (groups == null){
81       groups = new ThreadGroup[1];
82     } else {
83       ThreadGroup[] ng = new ThreadGroup[ngroups+1];
84       System.arraycopy(groups, 0, ng, 0, ngroups);
85       groups = ng;
86     }
87     
88     groups[ngroups++] = childGroup;
89   }
90   
91   private synchronized void remove (ThreadGroup childGroup){
92     if (!destroyed){
93       for (int i=0; i<ngroups; i++){
94         if (groups[i] == childGroup){
95           if (ngroups > 1){
96             int ngroups1 = ngroups - 1;
97             ThreadGroup[] ng = new ThreadGroup[ngroups1];
98             if (i > 0) {
99               System.arraycopy(groups, 0, ng, 0, i);
100             }
101             if (i < ngroups1) {
102               System.arraycopy(groups, i + 1, ng, i, ngroups - i);
103             }
104             groups = ng;
105             ngroups = ngroups1;
106             
107           } else {
108             groups = null;
109             ngroups = 0;
110           }
111           
112           if (nthreads == 0){
113             notifyAll();
114           }
115           
116           // check if its time for a suicide in case we are a daemon group
117           if (daemon){
118             if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)){
119               destroy();
120             }
121           }
122           
123           break;
124         }
125       }
126     }
127   }
128   
129   
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
132   // for ThreadInfo
133   
134   /**
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
137    */
138   synchronized void addUnstarted (){
139     if (destroyed){
140       throw new IllegalThreadStateException();
141     }
142
143     nUnstartedThreads++;
144   }
145   
146   /**
147    * this is called during Thread.start()
148    * NOTE - this is implemented as a native method to avoid shared field CGs
149    */
150   synchronized void add (Thread newThread){
151     if (destroyed){
152       throw new IllegalThreadStateException();
153     }
154
155     if (threads == null){
156       threads = new Thread[1];
157     } else {
158       Thread[] nt = new Thread[nthreads+1];
159       System.arraycopy(threads, 0, nt, 0, nthreads);
160       threads = nt;
161     }
162     
163     threads[nthreads++] = newThread;
164     nUnstartedThreads--;
165   }
166   
167   /**
168    * this is called automatically upon thread termination 
169    */
170   synchronized void threadTerminated (Thread terminatedThread){
171     if (!destroyed){
172       for (int i=0; i<nthreads; i++){
173         if (threads[i] == terminatedThread){
174           if (nthreads == 1){
175             threads = null;
176             nthreads = 0;
177             
178           } else {
179             int nthreads1 = nthreads - 1;
180             Thread[] a = new Thread[nthreads1];
181             if (i > 0) {
182               System.arraycopy(threads, 0, a, 0, i);
183             }
184             if (i < nthreads1) {
185               System.arraycopy(groups, i + 1, a, i, ngroups - i);
186             }
187             threads = a;
188             nthreads = nthreads1;
189           }
190
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
193           if (nthreads == 0) {
194             notifyAll();
195           }
196           if (daemon) {
197             if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) {
198               destroy();
199             }
200           }
201           
202           break;
203         }
204       }
205     }
206   }
207   
208   //--- public getters and setters
209   public final String getName(){
210     return name;
211   }
212   
213   public final ThreadGroup getParent(){
214     return parent;
215   }
216   
217   public final int getMaxPriority(){
218     return maxPriority;
219   }
220   
221   public final boolean isDaemon(){
222     return daemon;
223   }
224   
225   public synchronized boolean isDestroyed(){
226     return destroyed;
227   }
228   
229   public final void setDaemon (boolean daemon){
230     this.daemon = daemon;
231   }
232   
233   /**
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
236    */
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;
241       
242       for (int i=0; i<groups.length; i++){
243         groups[i].setMaxPriority(newMaxPriority);
244       }
245     }
246   }
247   
248   public final boolean parentOf (ThreadGroup tg){
249     while (true){
250       if (tg == this) return true;
251       ThreadGroup tgParent = tg.parent;
252       if (tgParent == null) return false;
253       tg = tgParent;
254     }
255   }
256   
257   public final void checkAccess(){
258     // not sure what to do here
259   }
260   
261   /**
262    * return number of threads including all sub-groups
263    */
264   public synchronized int activeCount() {
265     if (destroyed){
266       return 0;
267       
268     } else {
269       int nActive = nthreads;
270       if (ngroups > 0){
271         for (int i=0; i<ngroups; i++){
272           nActive += groups[i].activeCount();
273         }
274       }
275       
276       return nActive;
277     }
278   }
279   
280   /**
281    * copy threads into provided list
282    * kind of a misnomer
283    */
284   public int enumerate (Thread[] dst){
285     return enumerate(dst, 0, true);
286   }
287   
288   public int enumerate (Thread[] dst, boolean recurse){
289     return enumerate(dst, 0, recurse);
290   }
291   
292   private synchronized int enumerate (Thread[] dst, int idx, boolean recurse){
293     int n = 0;
294
295     int len = nthreads;
296     if ((idx + len) > dst.length){
297       len = dst.length - idx;
298     }
299     
300     for (int i = 0; i < len; i++) {
301       if (threads[i].isAlive()) {
302         dst[idx++] = threads[i];
303         n++;
304       }
305     }
306
307     if (recurse && (idx < dst.length)) {
308       for (int j = 0; j < ngroups; j++) {
309         n += groups[j].enumerate(dst, idx, true);
310       }
311     }
312     
313     return n;    
314   }
315   
316   public synchronized int activeGroupCount() {
317     if (destroyed){
318       return 0;
319       
320     } else {
321       int nActive = ngroups;
322       if (ngroups > 0){
323         for (int i=0; i<ngroups; i++){
324           nActive += groups[i].activeGroupCount();
325         }
326       }
327       
328       return nActive;
329     }
330   }
331   
332   public int enumerate (ThreadGroup[] dst){
333     return enumerate(dst, 0, true);
334   }
335   
336   public int enumerate (ThreadGroup[] dst, boolean recurse){
337     return enumerate(dst, 0, recurse);
338   }
339  
340   private synchronized int enumerate (ThreadGroup[] dst, int idx, boolean recurse){
341     int n = 0;
342
343     int len = ngroups;
344     if ((idx + len) > dst.length){
345       len = dst.length - idx;
346     }
347     
348     for (int i = 0; i < len; i++) {
349         dst[idx++] = groups[i];
350         n++;
351     }
352
353     if (recurse && (idx < dst.length)) {
354       for (int j = 0; j < ngroups; j++) {
355         n += groups[j].enumerate(dst, idx, true);
356       }
357     }
358     
359     return n;
360   }
361
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;
366   
367   public final void stop(){
368     if (doRecursively(OP_STOP)){
369       Thread.currentThread().stop();
370     }
371   }
372   public final void interrupt(){
373     doRecursively(OP_INTERRUPT);
374   }
375   public final void suspend(){
376     if (doRecursively(OP_SUSPEND)){
377       Thread.currentThread().suspend();      
378     }
379   }
380   public final void resume(){
381     doRecursively(OP_RESUME);
382   }
383   
384   private synchronized boolean doRecursively (int op){
385     boolean suicide = false;
386     
387     for (int i=0; i<nthreads; i++){
388       Thread t = threads[i];
389       switch (op){
390         case OP_STOP:
391           if (t == Thread.currentThread()) {
392             suicide = true; // defer
393           } else {
394             t.stop();
395           }
396           break;
397         case OP_INTERRUPT: t.interrupt(); break;
398         case OP_SUSPEND:
399           if (t == Thread.currentThread()) {
400             suicide = true; // defer
401           } else {
402             t.suspend();
403           }
404           break;
405         case OP_RESUME: t.resume(); break;
406       }
407     }
408     
409     for (int j=0; j<ngroups; j++){
410       suicide = suicide || groups[j].doRecursively(op);
411     }
412     
413     return suicide;
414   }
415   
416   
417   public synchronized final void destroy(){
418     if (destroyed || (nthreads > 0)) {
419       throw new IllegalThreadStateException();
420     }
421
422     for (int i=0; i<ngroups; i++){
423       groups[i].destroy();
424     }
425     
426     if (parent != null){ // no destroying the system group
427       nthreads = 0;
428       threads = null;
429       
430       ngroups = 0;
431       groups = null;
432       
433       destroyed = true;
434       
435       parent.remove(this);
436     }
437   }
438
439   //--- just for debugging
440   public void list(){
441     list( System.out, 0);
442   }
443   synchronized void list (PrintStream ps, int indent){
444     for (int i=0; i<indent; i++) ps.print(' ');
445     ps.println(toString());
446     
447     indent+= 4;
448     for (int j=0; j<nthreads; j++){
449       for (int i=0; i<indent; i++) ps.print(' ');
450       ps.println( threads[j]);
451     }
452     
453     for (int k=0; k<ngroups; k++){
454       groups[k].list( ps, indent);
455     }
456   }
457   
458   public boolean allowThreadSuspension (boolean allowSuspension){
459     vmAllowSuspension = allowSuspension;
460     return true;
461   }
462   
463   @Override
464   public String toString () {
465     return getClass().getName() + "[name=" + name + ",maxpri=" + maxPriority + ']';
466   }
467
468   // we handle this in ThreadInfo, but again this is a public method that could be called from anywhere
469   @Override
470   public void uncaughtException (Thread t, Throwable x) {
471     if (parent != null) { // if we have a parent, delegate
472       parent.uncaughtException(t, x);
473       
474     } else { // check if there is a default handler
475       Thread.UncaughtExceptionHandler xh = Thread.getDefaultUncaughtExceptionHandler();
476       if (xh != null) {
477         xh.uncaughtException(t, x);
478         
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);
483         }
484       }
485     }
486   }
487 }