Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / main / gov / nasa / jpf / vm / ThreadList.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 package gov.nasa.jpf.vm;
19
20
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.util.HashData;
23 import gov.nasa.jpf.util.Predicate;
24
25 import java.util.ArrayList;
26 import java.util.BitSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.NoSuchElementException;
30
31 /**
32  * Contains the list of all ThreadInfos for live java.lang.Thread objects
33  * 
34  * We add a thread upon creation (within the ThreadInfo ctor), and remove it
35  * when the corresponding java.lang.Thread object gets recycled by JPF. This means
36  * that:
37  *   * the thread list can contain terminated threads
38  *   * terminated and recycled threads are (eventually) removed from the list
39  *   * the list can shrink along a given path
40  *   * thread ids don't have to correspond with storing order !!
41  *   * thread ids can be re-used
42  *
43  * Per default, thread ids are re-used in order to be packed (which is required to efficiently
44  * keep track of referencing threads in ElementInfo reftids). If there is a need
45  * to avoid recycled thread ids, set 'vm.reuse_tid=false'.
46  * 
47  * NOTE - this ThreadList implementation doubles up as a thread object -> ThreadInfo
48  * map, which is for instance heavily used by the JPF_java_lang_Thread peer.
49  * 
50  * This implies that ThreadList is still not fully re-organized in case something
51  * keeps terminated thread objects alive. We could avoid this by having a separate
52  * map for live threads<->ThreadInfos, but this would also have to be a backrackable
53  * container that is highly redundant to ThreadList (the only difference being
54  * that terminated threads could be removed from ThreadList).
55  * 
56  */
57 public class ThreadList implements Cloneable, Iterable<ThreadInfo>, Restorable<ThreadList> {
58
59   public static class Count {
60     public final int alive;
61     public final int runnableNonDaemons;
62     public final int runnableDaemons;
63     public final int blocked;
64     
65     Count (int alive, int runnableNonDaemons, int runnableDaemons, int blocked){
66       this.alive = alive;
67       this.runnableNonDaemons = runnableNonDaemons;
68       this.runnableDaemons = runnableDaemons;
69       this.blocked = blocked;
70     }
71   }
72   
73   protected boolean reuseTid;
74   
75   // ThreadInfos for all created but not terminated threads
76   protected ThreadInfo[] threads;
77   
78   // the highest ID created so far along this path
79   protected int maxTid;
80
81
82   static class TListMemento implements Memento<ThreadList> {
83     // note that we don't clone/deepcopy ThreadInfos
84     Memento<ThreadInfo>[] tiMementos;
85     int maxTid;
86
87     TListMemento(ThreadList tl) {
88       ThreadInfo[] threads = tl.threads;
89       int len = threads.length;
90
91       maxTid = tl.maxTid;
92       tiMementos = new Memento[len];
93       for (int i=0; i<len; i++){
94         ThreadInfo ti = threads[i];
95         Memento<ThreadInfo> m = null;
96
97         if (!ti.hasChanged()){
98           m = ti.cachedMemento;
99         }
100         if (m == null){
101           m = ti.getMemento();
102           ti.cachedMemento = m;
103         }
104         tiMementos[i] = m;
105       }
106     }
107
108     @Override
109         public ThreadList restore(ThreadList tl){
110       int len = tiMementos.length;
111       ThreadInfo[] threads = new ThreadInfo[len];
112       for (int i=0; i<len; i++){
113         Memento<ThreadInfo> m = tiMementos[i];
114         ThreadInfo ti = m.restore(null);
115         ti.cachedMemento = m;
116         threads[i] = ti;
117         ti.tlIdx = i;
118       }
119       tl.threads = threads;
120       tl.maxTid = maxTid;
121
122       return tl;
123     }
124   }
125
126
127   protected ThreadList() {
128     // nothing here
129   }
130
131   /**
132    * Creates a new empty thread list.
133    */
134   public ThreadList (Config config, KernelState ks) {
135     threads = new ThreadInfo[0];
136     
137     reuseTid = config.getBoolean("vm.reuse_tid", false);
138   }
139
140   @Override
141   public Memento<ThreadList> getMemento(MementoFactory factory) {
142     return factory.getMemento(this);
143   }
144   public Memento<ThreadList> getMemento(){
145     return new TListMemento(this);
146   }
147
148   @Override
149   public Object clone() {
150     ThreadList other = new ThreadList();
151     other.threads = new ThreadInfo[threads.length];
152
153     for (int i=0; i<threads.length; i++) {
154       other.threads[i] = (ThreadInfo) threads[i].clone();
155     }
156
157     return other;
158   }
159
160   /**
161    * add a new ThreadInfo if it isn't already in the list.
162    * Note: the returned id is NOT our internal storage index
163    * 
164    * @return (path specific) Thread id
165    */
166   public int add (ThreadInfo ti) {
167     int n = threads.length;
168
169     BitSet ids = new BitSet();   
170     for (int i=0; i<n; i++) {
171       ThreadInfo t = threads[i];
172       if (t == ti) {
173         return t.getId();
174       }
175       
176       ids.set( t.getId());
177     }
178
179     // append it
180     ThreadInfo[] newThreads = new ThreadInfo[n+1];
181     System.arraycopy(threads, 0, newThreads, 0, n);
182     
183     newThreads[n] = ti;
184     ti.tlIdx = n;
185     
186     threads = newThreads;
187     
188     if (reuseTid){
189       return ids.nextClearBit(0);
190     } else {
191       return maxTid++;
192     }
193   }
194   
195   public boolean remove (ThreadInfo ti){
196     int n = threads.length;
197     
198     for (int i=0; i<n; i++) {
199       if (ti == threads[i]){
200         int n1 = n-1;
201         ThreadInfo[] newThreads = new ThreadInfo[n1];
202         if (i>0){
203           System.arraycopy(threads, 0, newThreads, 0, i);
204         }
205         if (i<n1){
206           System.arraycopy(threads, i+1, newThreads, i, (n1-i));
207           
208           // update the tlIdx values
209           for (int j=i; j<n1; j++){
210             ThreadInfo t = threads[j];
211             if (t != null){
212               t.tlIdx = j;
213             }
214           }
215         }
216         
217         threads = newThreads;        
218         return true;
219       }
220     }
221     
222     return false;
223   }
224
225   /**
226    * Returns the array of threads.
227    */
228   public ThreadInfo[] getThreads() {
229     return threads.clone();
230   }
231
232   public void hash (HashData hd) {
233     for (int i=0; i<threads.length; i++){
234       threads[i].hash(hd);
235     }
236   }
237   
238   public ThreadInfo getThreadInfoForId (int tid){
239     for (int i=0; i<threads.length; i++){
240       ThreadInfo ti = threads[i];
241       if (ti.getId() == tid){
242         return ti;
243       }
244     }
245     
246     return null;
247   }
248
249   public ThreadInfo getThreadInfoForObjRef (int objRef){
250     for (int i=0; i<threads.length; i++){
251       ThreadInfo ti = threads[i];
252       if (ti.getThreadObjectRef() == objRef){
253         return ti;
254       }
255     }
256     
257     return null;
258   }
259   
260   public boolean contains (ThreadInfo ti){
261     int idx = ti.tlIdx;
262     
263     if (idx < threads.length){
264       return threads[idx] == ti;
265     }
266     
267     return false;
268   }
269   
270   /**
271    * Returns the length of the list.
272    */
273   public int length () {
274     return threads.length;
275   }
276
277   /**
278    * Replaces the array of ThreadInfos.
279    */
280   public void setAll(ThreadInfo[] threads) {
281     this.threads = threads;
282   }
283
284   public ThreadInfo locate (int objref) {
285     for (int i = 0, l = threads.length; i < l; i++) {
286       if (threads[i].getThreadObjectRef() == objref) {
287         return threads[i];
288       }
289     }
290
291     return null;
292   }
293
294   public void markRoots (Heap heap) {
295     for (int i = 0, l = threads.length; i < l; i++) {
296       if (threads[i].isAlive()) {
297         threads[i].markRoots(heap);
298       }
299     }
300   }
301   
302   public boolean hasProcessTimeoutRunnables (ApplicationContext appCtx){
303     for (int i = 0; i < threads.length; i++) {
304       ThreadInfo ti = threads[i];
305       if (ti.isTimeoutRunnable() && ti.getApplicationContext() == appCtx) {
306         return true;
307       }
308     }
309     return false;
310   }
311   
312   public ThreadInfo[] getProcessTimeoutRunnables (ApplicationContext appCtx){
313     ArrayList<ThreadInfo> list = new ArrayList<ThreadInfo>();
314     
315     for (int i = 0; i < threads.length; i++) {
316       ThreadInfo ti = threads[i];
317       if (ti.isTimeoutRunnable() && ti.getApplicationContext() == appCtx) {
318         list.add(ti);
319       }
320     }
321     
322     return list.toArray( new ThreadInfo[list.size()]);
323   }
324   
325   public boolean hasLiveThreads(){
326     for (int i = 0; i < threads.length; i++) {
327       if (threads[i].isAlive()) {
328         return true;
329       }
330     }
331     return false;    
332   }
333   
334   public boolean hasTimeoutRunnables (){
335     for (int i = 0; i < threads.length; i++) {
336       if (threads[i].isRunnable()) {
337         return true;
338       }
339     }
340     return false;
341   }
342   
343   public boolean hasUnblockedThreads(){
344     for (int i = 0; i < threads.length; i++) {
345       if (threads[i].isUnblocked()) {
346         return true;
347       }
348     }
349     return false;    
350   }
351
352   public ThreadInfo[] getTimeoutRunnables (){
353     ArrayList<ThreadInfo> list = new ArrayList<ThreadInfo>();
354     
355     for (int i = 0; i < threads.length; i++) {
356       ThreadInfo ti = threads[i];
357       if (ti.isTimeoutRunnable()) {
358         list.add(ti);
359       }
360     }
361     
362     return list.toArray( new ThreadInfo[list.size()]);
363   }
364
365   
366
367   public boolean hasAnyMatching(Predicate<ThreadInfo> predicate) {
368     for (int i = 0, l = threads.length; i < l; i++) {
369       if (predicate.isTrue(threads[i])) {
370         return true;
371       }
372     }
373     
374     return false;
375   }
376   
377   public boolean hasAnyMatchingOtherThan(ThreadInfo ti, Predicate<ThreadInfo> predicate) {
378     for (int i = 0, l = threads.length; i < l; i++) {
379       if(ti != threads[i]) {
380         if (predicate.isTrue(threads[i])) {
381           return true;
382         }
383       }
384     }
385     
386     return false;
387   }
388   
389   public boolean hasOnlyMatching(Predicate<ThreadInfo> predicate) {
390     for (int i = 0, l = threads.length; i < l; i++) {
391       if (!predicate.isTrue(threads[i])) {
392         return false;
393       }
394     }
395     
396     return true;
397   }
398   
399   public boolean hasOnlyMatchingOtherThan(ThreadInfo ti, Predicate<ThreadInfo> predicate) {
400     int n=0;
401     for (int i = 0, l = threads.length; i < l; i++) {
402       if(ti != threads[i]) {
403         if (!predicate.isTrue(threads[i])) {
404           return false;
405         } else {
406           n++;
407         }
408       }
409     }
410     
411     return (n>0);
412   }
413   
414   public ThreadInfo[] getAllMatching(Predicate<ThreadInfo> predicate) {
415     List<ThreadInfo> list = new ArrayList<ThreadInfo>();
416     
417     int n = 0;
418     for (int i = 0, l = threads.length; i < l; i++) {
419       ThreadInfo ti = threads[i];
420       if (predicate.isTrue(ti)) {
421         list.add(ti);
422         n++;
423       }
424     }
425     
426     return list.toArray(new ThreadInfo[n]);
427   }
428
429   public ThreadInfo[] getAllMatchingWith(final ThreadInfo ti, Predicate<ThreadInfo> predicate) {
430     List<ThreadInfo> list = new ArrayList<ThreadInfo>();
431     
432     int n = 0;
433     for (int i = 0, l = threads.length; i < l; i++) {
434       ThreadInfo t = threads[i];
435       if (predicate.isTrue(t) || (ti==t)) {
436         list.add(t);
437         n++;
438       }
439     }
440     
441     return list.toArray(new ThreadInfo[n]);
442   }
443   
444   public ThreadInfo[] getAllMatchingWithout(final ThreadInfo ti, Predicate<ThreadInfo> predicate) {
445     List<ThreadInfo> list = new ArrayList<ThreadInfo>();
446     
447     int n = 0;
448     for (int i = 0, l = threads.length; i < l; i++) {
449       ThreadInfo t = threads[i];
450       if (predicate.isTrue(t) && (ti != t)) {
451         list.add(t);
452         n++;
453       }
454     }
455     
456     return list.toArray(new ThreadInfo[n]);
457   }
458   
459   public int getMatchingCount(Predicate<ThreadInfo> predicate) {
460     int n = 0;
461     for (int i = 0, l = threads.length; i < l; i++) {
462       ThreadInfo ti = threads[i];
463       if (predicate.isTrue(ti)) {
464         n++;
465       }
466     }
467     
468     return n;
469   }
470   
471   public ThreadInfo getFirstMatching(Predicate<ThreadInfo> predicate) {
472     for (int i = 0, l = threads.length; i < l; i++) {
473       ThreadInfo t = threads[i];
474       if (predicate.isTrue(t)) {
475         return t;
476       }
477     }
478     
479     return null;
480   }
481   
482   public Count getCountWithout (ThreadInfo tiExclude){
483     int alive=0, runnableNonDaemons=0, runnableDaemons=0, blocked=0;
484     
485     for (int i = 0; i < threads.length; i++) {
486       ThreadInfo ti = threads[i];
487   
488       if (ti != tiExclude){
489         if (ti.isAlive()) {
490           alive++;
491
492           if (ti.isRunnable()) {
493             if (ti.isDaemon()) {
494               runnableDaemons++;
495             } else {
496               runnableNonDaemons++;
497             }
498           } else {
499             blocked++;
500           }
501         }
502       }
503     }
504     
505     return new Count(alive, runnableNonDaemons, runnableDaemons, blocked);
506   }
507
508   public Count getCount(){
509     return getCountWithout(null);
510   }
511
512   public void dump () {
513     int i=0;
514     for (ThreadInfo t : threads) {
515       System.err.println("[" + i++ + "] " + t);
516     }
517   }
518
519   @Override
520   public Iterator<ThreadInfo> iterator() {
521     return new Iterator<ThreadInfo>() {
522       int i = 0;
523
524       @Override
525         public boolean hasNext() {
526         return threads != null && threads.length>0 && i<threads.length;
527       }
528
529       @Override
530         public ThreadInfo next() {
531         if (threads != null && threads.length>0 && i<threads.length){
532           return threads[i++];
533         } else {
534           throw new NoSuchElementException();
535         }
536       }
537
538       @Override
539         public void remove() {
540         throw new UnsupportedOperationException("Iterator<ThreadInfo>.remove()");
541       }
542     };
543   }
544
545   
546   class CanonicalLiveIterator implements Iterator<ThreadInfo> {
547     
548     int nextGid = -1;
549     int nextIdx = -1;
550     
551     CanonicalLiveIterator(){
552       setNext();
553     }
554     
555     // <2do> not overly efficient, but we assume small thread lists anyways
556     void setNext (){
557       int lastGid = nextGid;
558       int nextGid = Integer.MAX_VALUE;
559       int nextIdx = -1;
560       
561       for (int i=0; i<threads.length; i++){
562         ThreadInfo ti = threads[i];
563         if (ti.isAlive()){
564           int gid = ti.getGlobalId();
565           if ((gid > lastGid) && (gid < nextGid)){
566             nextGid = gid;
567             nextIdx = i;
568           }
569         }
570       }
571       
572       CanonicalLiveIterator.this.nextGid = nextGid;
573       CanonicalLiveIterator.this.nextIdx = nextIdx;
574     }
575     
576     @Override
577         public boolean hasNext() {
578       return (nextIdx >= 0);
579     }
580
581     @Override
582         public ThreadInfo next() {
583       if (nextIdx >= 0){
584         ThreadInfo tiNext = threads[nextIdx];
585         setNext();
586         return tiNext;
587         
588       } else {
589         throw new NoSuchElementException();
590       }
591     }
592
593     @Override
594         public void remove() {
595       throw new UnsupportedOperationException("Iterator<ThreadInfo>.remove()");
596     }
597   }
598   
599   /**
600    * an iterator for a canonical order over all live threads
601    */
602   public Iterator<ThreadInfo> canonicalLiveIterator(){
603     return new CanonicalLiveIterator();     
604   }
605   
606   
607   /**
608    * only for debugging purposes, this is expensive
609    */
610   public void checkConsistency(boolean isStore) {
611     for (int i = 0; i < threads.length; i++) {
612       ThreadInfo ti = threads[i];
613       
614       ti.checkConsistency(isStore);
615     }
616   }
617 }