001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.mmtk.plan;
014    
015    import org.mmtk.utility.Constants;
016    
017    import org.mmtk.vm.Monitor;
018    import org.mmtk.vm.VM;
019    
020    import org.vmmagic.pragma.*;
021    
022    /**
023     * This class represents a pool of collector contexts that can be triggered
024     * to perform collection activity.
025     */
026    @Uninterruptible
027    public class ParallelCollectorGroup implements Constants {
028    
029      /****************************************************************************
030       * Instance fields
031       */
032    
033      /** The name of this collector context group. */
034      private final String name;
035    
036      /** The collector context instances operating within this group */
037      private ParallelCollector[] contexts;
038    
039      /** Lock used to manage group state. */
040      private Monitor lock;
041    
042      /** The number of cycles triggered */
043      private volatile int triggerCount;
044    
045      /** The number of threads that are currently parked */
046      private volatile int contextsParked;
047    
048      /** Is there an abort request outstanding? */
049      private volatile boolean aborted;
050    
051      /** Used to count threads during calls to rendezvous() */
052      private int[] rendezvousCounter = new int[2];
053    
054      /** Which rendezvous counter is currently in use */
055      private volatile int currentRendezvousCounter;
056    
057      /****************************************************************************
058       *
059       * Initialization
060       */
061      public ParallelCollectorGroup(String name) {
062        this.name = name;
063      }
064    
065      /**
066       * @return The number of active collector contexts.
067       */
068      public int activeWorkerCount() {
069        return contexts.length;
070      }
071    
072      /**
073       * Initialize the collector context group.
074       *
075       * @param size The number of collector contexts within the group.
076       * @param klass The type of collector context to create.
077       */
078      @Interruptible
079      public void initGroup(int size, Class<? extends ParallelCollector> klass) {
080        this.lock = VM.newHeavyCondLock("CollectorContextGroup");
081        this.triggerCount = 1;
082        this.contexts = new ParallelCollector[size];
083        for(int i = 0; i < size; i++) {
084          try {
085            contexts[i] = klass.newInstance();
086            contexts[i].group = this;
087            contexts[i].workerOrdinal = i;
088            VM.collection.spawnCollectorContext(contexts[i]);
089          } catch (Throwable t) {
090            VM.assertions.fail("Error creating collector context '" + klass.getName() + "' for group '" + name + "': " + t.toString());
091          }
092        }
093      }
094    
095      /**
096       * Wake up the parked threads in this group.
097       */
098      public void triggerCycle() {
099        lock.lock();
100        triggerCount++;
101        contextsParked = 0;
102        lock.broadcast();
103        lock.unlock();
104      }
105    
106      /**
107       * Signal that you would like the threads to park abruptly. Has no effect if no cycle is active.
108       */
109      public void abortCycle() {
110        lock.lock();
111        if (contextsParked < contexts.length) {
112          aborted = true;
113        }
114        lock.unlock();
115      }
116    
117      /**
118       * Has the cycle been aborted?
119       */
120      public boolean isAborted() {
121        return aborted;
122      }
123    
124      /**
125       * Wait until the group is idle.
126       */
127      public void waitForCycle() {
128        lock.lock();
129        while (contextsParked < contexts.length) {
130          lock.await();
131        }
132        lock.unlock();
133      }
134    
135      /**
136       * Park the given collector in the group. The given context must be a member of this group.
137       *
138       * @param context The context to park.
139       */
140      public void park(ParallelCollector context) {
141        if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isMember(context));
142        lock.lock();
143        context.lastTriggerCount++;
144        if (context.lastTriggerCount == triggerCount) {
145          contextsParked++;
146          if (contextsParked == contexts.length) {
147            aborted = false;
148          }
149          lock.broadcast();
150          while (context.lastTriggerCount == triggerCount) {
151            lock.await();
152          }
153        }
154        lock.unlock();
155      }
156    
157      /**
158       * Is the given context and member of this group.
159       *
160       * @param context The context to pass.
161       * @return {@code true} if the context is a member.
162       */
163      public boolean isMember(CollectorContext context) {
164        for(CollectorContext c: contexts) {
165          if (c == context) {
166            return true;
167          }
168        }
169        return false;
170      }
171    
172      /**
173       * Rendezvous with other active threads in this group.
174       *
175       * @return The order in which you entered the rendezvous.
176       */
177      public int rendezvous() {
178        lock.lock();
179        int i = currentRendezvousCounter;
180        int me = rendezvousCounter[i]++;
181        if (me == contexts.length-1) {
182          currentRendezvousCounter ^= 1;
183          rendezvousCounter[currentRendezvousCounter] = 0;
184          lock.broadcast();
185        } else {
186          while(rendezvousCounter[i] < contexts.length) {
187            lock.await();
188          }
189        }
190        lock.unlock();
191        return me;
192      }
193    }