/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.plan; import org.mmtk.vm.Monitor; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; /** * This class represents a pool of collector contexts that can be triggered * to perform collection activity. */ @Uninterruptible public class ParallelCollectorGroup { /**************************************************************************** * Instance fields */ /** The name of this collector context group. */ private final String name; /** The collector context instances operating within this group */ private ParallelCollector[] contexts; /** Lock used to manage group state. */ private Monitor lock; /** The number of cycles triggered */ private volatile int triggerCount; /** The number of threads that are currently parked */ private volatile int contextsParked; /** Is there an abort request outstanding? */ private volatile boolean aborted; /** Used to count threads during calls to rendezvous() */ private final int[] rendezvousCounter = new int[2]; /** Which rendezvous counter is currently in use */ private volatile int currentRendezvousCounter; /**************************************************************************** * * Initialization */ /** * @param name human-readable name of the collector group */ public ParallelCollectorGroup(String name) { this.name = name; } /** * @return The number of active collector contexts. */ public int activeWorkerCount() { return contexts.length; } /** * Initialize the collector context group. * * @param size The number of collector contexts within the group. * @param klass The type of collector context to create. */ @Interruptible public void initGroup(int size, Class<? extends ParallelCollector> klass) { this.lock = VM.newHeavyCondLock("CollectorContextGroup"); this.triggerCount = 1; this.contexts = new ParallelCollector[size]; for (int i = 0; i < size; i++) { try { contexts[i] = klass.newInstance(); contexts[i].group = this; contexts[i].workerOrdinal = i; VM.collection.spawnCollectorContext(contexts[i]); } catch (Throwable t) { VM.assertions.fail("Error creating collector context '" + klass.getName() + "' for group '" + name + "': " + t.toString()); } } } /** * Wake up the parked threads in this group. */ public void triggerCycle() { lock.lock(); triggerCount++; contextsParked = 0; lock.broadcast(); lock.unlock(); } /** * Signal that you would like the threads to park abruptly. Has no effect if no cycle is active. */ public void abortCycle() { lock.lock(); if (contextsParked < contexts.length) { aborted = true; } lock.unlock(); } /** * @return whether the cycle has been aborted */ public boolean isAborted() { return aborted; } /** * Wait until the group is idle. */ public void waitForCycle() { lock.lock(); while (contextsParked < contexts.length) { lock.await(); } lock.unlock(); } /** * Park the given collector in the group. The given context must be a member of this group. * * @param context The context to park. */ public void park(ParallelCollector context) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isMember(context)); lock.lock(); context.lastTriggerCount++; if (context.lastTriggerCount == triggerCount) { contextsParked++; if (contextsParked == contexts.length) { aborted = false; } lock.broadcast(); while (context.lastTriggerCount == triggerCount) { lock.await(); } } lock.unlock(); } /** * Is the given context and member of this group. * * @param context The context to pass. * @return {@code true} if the context is a member. */ public boolean isMember(CollectorContext context) { for (CollectorContext c: contexts) { if (c == context) { return true; } } return false; } /** * Rendezvous with other active threads in this group. * * @return The order in which you entered the rendezvous. */ public int rendezvous() { lock.lock(); int i = currentRendezvousCounter; int me = rendezvousCounter[i]++; if (me == contexts.length - 1) { currentRendezvousCounter ^= 1; rendezvousCounter[currentRendezvousCounter] = 0; lock.broadcast(); } else { while (rendezvousCounter[i] < contexts.length) { lock.await(); } } lock.unlock(); return me; } }