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