/* * 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.harness.scheduler.javathreads; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.mmtk.harness.Collector; import org.mmtk.harness.Mutator; import org.mmtk.harness.lang.Trace; import org.mmtk.harness.lang.Trace.Item; import org.mmtk.harness.scheduler.MMTkThread; import org.mmtk.harness.scheduler.Schedulable; import org.mmtk.harness.scheduler.ThreadModel; import static org.mmtk.harness.scheduler.ThreadModel.State.*; import org.mmtk.utility.Log; import org.mmtk.vm.VM; public final class JavaThreadModel extends ThreadModel { static { //Trace.enable(Item.SCHEDULER); } /** * Collector threads scheduled through #scheduleCollector(Schedulable) */ private final Set<CollectorThread> collectorThreads = Collections.synchronizedSet(new HashSet<CollectorThread>()); /** * Mutator threads scheduled through scheduleMutator(Schedulable) */ private final Set<MutatorThread> mutatorThreads = Collections.synchronizedSet(new HashSet<MutatorThread>()); /** * Create a new mutator thread */ @Override public void scheduleMutator(Schedulable code) { Trace.trace(Item.SCHEDULER, "Scheduling new mutator"); MutatorThread t = new MutatorThread(this,code); mutatorThreads.add(t); t.start(); } /** * Create a new collector thread */ @Override public void scheduleCollector() { Trace.trace(Item.SCHEDULER, "Scheduling new collector"); CollectorThread t = new CollectorThread(); collectorThreads.add(t); t.start(); } /** * Create a new collector thread with a specific Schedulable * * Used for scheduling unit tests in collector context */ @Override public Thread scheduleCollector(Schedulable code) { Trace.trace(Item.SCHEDULER, "Scheduling new collector"); CollectorContextThread t = new CollectorContextThread(this,code); collectorThreads.add(t); t.start(); return t; } void removeCollector(CollectorThread c) { collectorThreads.remove(c); } private MMTkThread currentMMTkThread() { return ((MMTkThread)Thread.currentThread()); } @Override public void yield() { if (isRunning()) { if (currentMMTkThread().yieldPolicy()) { Thread.yield(); } } } @Override public Log currentLog() { return currentMMTkThread().getLog(); } @Override public Mutator currentMutator() { assert Thread.currentThread() instanceof MutatorThread : "Current thread is not a Mutator"; return ((MutatorThread)Thread.currentThread()).env; } /** * Perform the delicate operation of joining the pool of active mutators. * * If there isn't currently a GC in progress (!allWaitingForGC()), increment * the active mutator count and return. If a GC has been initiated, we will * join it at the next GC-safe point. * * Otherwise, we 'quietly' join the active GC, by incrementing the count of * waiting mutators, and calling waitForGC(false) (there has already been a 'last' * mutator). Once the GC has completed, we remove ourselves from the GC, * and increment the activeMutators. */ void joinMutatorPool() { synchronized (count) { if (!allWaitingForGC()) { incActiveMutators(); return; } mutatorsWaitingForGC++; incActiveMutators(); } waitForGC(false); synchronized (count) { mutatorsWaitingForGC--; } } private void incActiveMutators() { activeMutators++; count.notify(); } private void decActiveMutators() { activeMutators--; if (activeMutators == 0) count.notify(); } /** * Perform the delicate operation of leaving the mutator pool. * * If there's a GC scheduled, and we are the last thread to join, * join the GC (because we are required to trigger it) and then exit. * Otherwise, just decrement the mutator count and leave. */ void leaveMutatorPool(MutatorThread m) { Trace.trace(Item.SCHEDULER, "%d Leaving mutator pool", Thread.currentThread().getId()); synchronized (count) { boolean lastToGC = (mutatorsWaitingForGC == (activeMutators - 1)); if (!lastToGC) { decActiveMutators(); return; } mutatorsWaitingForGC++; } waitForGC(true); synchronized (count) { mutatorsWaitingForGC--; decActiveMutators(); } mutatorThreads.remove(m); } /** Synchronisation object used for GC triggering */ private final Object trigger = new Object(); /** * Wait for a GC to complete */ private void waitForGC(boolean last) { Trace.trace(Item.SCHEDULER, "%d waitForGC in", Thread.currentThread().getId()); synchronized (trigger) { if (last) { setState(GC); trigger.notifyAll(); } while (inGC > 0) { try { trigger.wait(); } catch (InterruptedException ie) {} } } Trace.trace(Item.SCHEDULER, "%d waitForGC out", Thread.currentThread().getId()); } @Override public void waitForGC() { boolean allWaiting; synchronized (count) { mutatorsWaitingForGC++; allWaiting = allWaitingForGC(); } waitForGC(allWaiting); synchronized (count) { mutatorsWaitingForGC--; } } /** * Check whether all mutators are in GC - MUST HOLD THE 'count' MONITOR * @return true if all mutators are waiting for GC */ private boolean allWaitingForGC() { return (activeMutators > 0) && (mutatorsWaitingForGC == activeMutators); } /** * Trigger a collection for the given reason */ @Override public void triggerGC(int why) { synchronized (trigger) { triggerReason = why; inGC = collectorThreads.size(); setState(BEGIN_GC); trigger.notifyAll(); } } /** * A GC thread has completed its GC work. */ @Override public void exitGC() { synchronized (trigger) { inGC--; if (inGC == 0) { setState(MUTATOR); trigger.notifyAll(); } } } @Override public void waitForGCStart() { synchronized(trigger) { while(inGC == 0 || !isState(GC)) { try { trigger.wait(); } catch (InterruptedException ie) {} } Trace.trace(Item.SCHEDULER, "GC has started"); } } /** Object used for synchronizing mutatorsWaitingForGC and activeMutators */ private static final Object count = new Object(); /** The number of mutators waiting for a collection to proceed. */ protected int mutatorsWaitingForGC; /** The number of collectors executing GC */ protected int inGC; /** The number of mutators currently executing in the system. */ protected int activeMutators; /** Thread access to current collector */ private static final ThreadLocal<Collector> collectorThreadLocal = new ThreadLocal<Collector>(); @Override public int rendezvous(int where) { return Rendezvous.rendezvous(Integer.toString(where),VM.activePlan.collectorCount()); } @Override public int mutatorRendezvous(String where, int expected) { synchronized (count) { mutatorsWaitingForGC++; } int ordinal = Rendezvous.rendezvous(where,expected); synchronized (count) { mutatorsWaitingForGC--; } return ordinal; } @Override public Collector currentCollector() { return collectorThreadLocal.get(); } static void setCurrentCollector(Collector c) { collectorThreadLocal.set(c); } @Override public JavaLock newLock(String name) { return new org.mmtk.harness.scheduler.javathreads.JavaLock(name); } /** * Wait for the mutator threads to exit. * @see org.mmtk.harness.scheduler.ThreadModel#schedule() */ @Override public void schedule() { startRunning(); /* Wait for the mutators to start */ while (mutatorThreads.size() > activeMutators) { synchronized (count) { try { count.wait(); } catch (InterruptedException e) { } Trace.trace(Item.SCHEDULER,"Active mutators = "+activeMutators); } } /* Wait for the mutators to exit */ while (activeMutators > 0) { synchronized (count) { try { count.wait(); } catch (InterruptedException e) { } Trace.trace(Item.SCHEDULER,"Active mutators = "+activeMutators); } } } @Override public void scheduleGcThreads() { synchronized (trigger) { startRunning(); inGC = collectorThreads.size(); setState(GC); trigger.notifyAll(); while (!isState(MUTATOR)) { try { trigger.wait(); } catch (InterruptedException e) { } } stopRunning(); } } @Override public boolean noThreadsInGC() { return inGC == 0; } @Override public boolean gcTriggered() { return inGC > 0; } }