/*
* 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.runtime.VM_Magic;
import static org.jikesrvm.runtime.VM_SysCall.sysCall;
import org.jikesrvm.scheduler.VM_Processor;
import org.jikesrvm.scheduler.VM_Scheduler;
import org.jikesrvm.scheduler.greenthreads.VM_GreenProcessor;
import org.jikesrvm.scheduler.greenthreads.VM_GreenScheduler;
import org.jikesrvm.scheduler.greenthreads.VM_GreenThread;
import org.vmmagic.pragma.Uninterruptible;
/**
* A synchronization barrier used to synchronize collector threads,
* and the VM_Processors they are running on, during parallel collections.
*
* The core barrier functionality is implemented by a barrier object.
* The code in this class is in charge of VM-related idiosyncrasies like
* computing how many processors are participating in a particular collection.
*/
public final class SynchronizationBarrier {
private static final int verbose = 0;
// number of physical processors on running computer
private int numRealProcessors;
final Barrier barrier = new Barrier();
/**
* Constructor
*/
public SynchronizationBarrier() {
// initialize numRealProcessors to 1. Will be set to actual value later.
// Using without resetting will cause waitABit() to yield instead of spinning
numRealProcessors = 1;
}
/**
* Wait for all other collectorThreads/processors to arrive at this barrier.
*/
@Uninterruptible
public int rendezvous(int where) {
barrier.arrive(where);
VM_Magic.isync(); // so subsequent instructions won't see stale values
// XXX This should be changed to return ordinal of current rendezvous rather than the one at the beginning
return VM_Magic.threadAsCollectorThread(VM_Scheduler.getCurrentThread()).getGCOrdinal();
}
/**
* First rendezvous for a collection, called by all CollectorThreads that arrive
* to participate in a collection. Thread with gcOrdinal==1 is responsible for
* detecting RVM processors stuck in Native C, blocking them in Native, and making
* them non-participants in the collection (by setting their counters to -1).
* Other arriving collector threads just wait until all have either arrived or
* been declared non-participating.
*/
@Uninterruptible
public void startupRendezvous() {
int myProcessorId = VM_Processor.getCurrentProcessorId();
VM_CollectorThread th = VM_Magic.threadAsCollectorThread(VM_Scheduler.getCurrentThread());
int myNumber = th.getGCOrdinal();
if (verbose > 0) {
VM.sysWriteln("GC Message: SynchronizationBarrier.startupRendezvous: proc ",
myProcessorId,
" ordinal ",
myNumber);
}
if (myNumber > 1) { // non-designated guys just wait for designated guy to finish
barrier.arrive(8888); // wait for designated guy to do his job
VM_Magic.isync(); // so subsequent instructions won't see stale values
if (verbose > 0) VM.sysWriteln("GC Message: startupRendezvous leaving as ", myNumber);
return; // leave barrier
}
// Thread with gcOrdinal==1 must detect processors whose active threads are
// stuck in Native C, block them there, and make them non-participants
//
waitABit(5); // give missing threads a chance to show up
int numParticipating = 0;
for (int i = 1; i <= VM_GreenScheduler.numProcessors; i++) {
if (VM_GreenScheduler.processors[i].lockInCIfInC()) { // can't be true for self
if (verbose > 0) VM.sysWriteln("GC Message: excluding processor ", i);
removeProcessor(i);
} else {
numParticipating++;
}
}
if (verbose > 0) {
VM.sysWriteln("GC Message: startupRendezvous numParticipating = ", numParticipating);
}
barrier.setTarget(numParticipating);
barrier.arrive(8888); // all setup now complete and we can proceed
VM_Magic.sync(); // update main memory so other processors will see it in "while" loop
VM_Magic.isync(); // so subsequent instructions won't see stale values
if (verbose > 0) {
VM.sysWriteln("GC Message: startupRendezvous designated proc leaving");
}
} // startupRendezvous
/**
* reset the rendezvous counters for all VPs to 0.
* Also sets numRealProcessors to number of real CPUs.
*/
@Uninterruptible
public void resetRendezvous() {
numRealProcessors = sysCall.sysNumProcessors();
barrier.clearTarget();
VM_Magic.sync(); // make other threads/processors see the update
}
/**
* method to give a waiting thread/processor something do without interferring
* with other waiting threads or those trying to enter the rendezvous.
* Spins if running with fewer RVM "processors" than physical processors.
* Yields (to Operating System) if running with more "processors" than
* real processors.
*
* @param x amount to spin in some unknown units
*/
@Uninterruptible
private int waitABit(int x) {
int sum = 0;
if (VM_GreenScheduler.numProcessors < numRealProcessors) {
// spin for a while, keeping the operating system thread
for (int i = 0; i < (x * 100); i++) {
sum = sum + i;
}
return sum;
} else {
sysCall.sysVirtualProcessorYield(); // pthread yield
return 0;
}
}
/**
* remove a processor from the rendezvous for the current collection.
* The removed processor in considered a "non-participant" for the collection.
*
* @param id processor id of processor to be removed.
*/
@Uninterruptible
private void removeProcessor(int id) {
VM_GreenProcessor vp = VM_GreenScheduler.processors[id];
// get processors collector thread off its transfer queue
vp.collectorThreadMutex.lock("removing a processor from gc");
VM_GreenThread ct = vp.collectorThread;
vp.collectorThread = null;
vp.collectorThreadMutex.unlock();
if (VM.VerifyAssertions) {
VM._assert(ct != null && ct.isGCThread());
}
// put it back on the global collector thread queue
VM_GreenScheduler.collectorMutex.lock("collector mutex for processor removal");
VM_GreenScheduler.collectorQueue.enqueue(ct);
VM_GreenScheduler.collectorMutex.unlock();
// set VPs CollectorThread ordinal number negative to indicate not participating
VM_CollectorThread.collectorThreads[id].setGCOrdinal(-1);
VM_Magic.sync(); // make other threads/processors see the update
} // removeProcessor
}