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}