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 }