/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * 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/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.memorymanagers.mminterface; import org.jikesrvm.VM; import org.jikesrvm.mm.mmtk.SynchronizedCounter; import org.jikesrvm.runtime.VM_Magic; import org.jikesrvm.runtime.VM_Time; import org.vmmagic.pragma.Uninterruptible; /** * This class implements barrier synchronization. * The mechanism handles proper resetting by usnig 3 underlying counters * and supports unconditional blocking until the number of participants * can be determined. */ @Uninterruptible final class Barrier { public static final int VERBOSE = 0; // The value of target is one more than the number of threads we expect to arrive. // It is one greater to allow safely updating the currentCounter value. // If target is -1, no thread can proceed past the barrier. // 3 counters are needed to support proper resetting without race conditions. // private volatile int target = -1; private static final int NUM_COUNTERS = 3; final SynchronizedCounter[] counters; SynchronizedCounter currentCounter; // Debugging constants private static final long WARN_PERIOD = VM_Time.secsToNanos(20); // Print msg every WARN_PERIOD seconds private static final long TIME_OUT = 3 * WARN_PERIOD; // Die after TIME_OUT seconds; public Barrier() { counters = new SynchronizedCounter[NUM_COUNTERS]; for (int i = 0; i < NUM_COUNTERS; i++) { counters[i] = new SynchronizedCounter(); } currentCounter = new SynchronizedCounter(); } // Set target to appropriate value // public void setTarget(int t) { VM_Magic.isync(); if (VM.VerifyAssertions) VM._assert(t >= 0); target = t + 1; VM_Magic.sync(); } public void clearTarget() { VM_Magic.isync(); target = -1; VM_Magic.sync(); } // Returns whether caller was first to arrive. // The coding to ensure resetting is delicate. // public int arrive(int where) { VM_Magic.isync(); int cur = currentCounter.peek(); SynchronizedCounter c = counters[cur]; int myValue = c.increment(); // Do NOT use the currentCounter variables unless designated thread if (VERBOSE >= 1) { VM.sysWriteln(where,": myValue = ",myValue); } if (VM.VerifyAssertions) VM._assert(myValue >= 0 && (target == -1 || myValue <= target)); if (myValue + 2 == target) { // last one to show up int next = (cur + 1) % NUM_COUNTERS; int prev = (cur - 1 + NUM_COUNTERS) % NUM_COUNTERS; counters[prev].reset(); // everyone has seen the value so safe to reset now if (next == 0) { currentCounter.reset(); // everyone has arrived but still waiting } else { currentCounter.increment(); } c.increment(); // now safe to let others past barrier VM_Magic.sync(); return myValue; } else { // everyone else long startNano = 0; long lastElapsedNano = 0; while (true) { long startCycles = VM_Time.cycles(); long endCycles = startCycles + ((long) 1e9); // a few hundred milliseconds more or less. long nowCycles; do { if (target != -1 && c.peek() == target) { VM_Magic.sync(); return myValue; } nowCycles = VM_Time.cycles(); } while (startCycles < nowCycles && nowCycles < endCycles); /* check against both ends to guard against CPU migration */ /* * According to the cycle counter, we've been spinning for a while. * Time to check nanoTime and see if we should print a warning and/or sysFail. */ if (startNano == 0) { startNano = VM_Time.nanoTime(); } else { long nowNano = VM_Time.nanoTime(); long elapsedNano = nowNano - startNano; if (elapsedNano - lastElapsedNano > WARN_PERIOD) { VM.sysWrite("GC Warning: Barrier wait has reached ",VM_Time.nanosToSecs(elapsedNano), " seconds. Called from "); VM.sysWrite(where,". myOrder = ",myValue," count is "); VM.sysWriteln(c.peek()," waiting for ",target - 1); lastElapsedNano = elapsedNano; } if (elapsedNano > TIME_OUT) { VM.sysFail("GC Error: Barrier Timeout"); } } } } } }