package ovm.core.services.memory;

import ovm.core.domain.DomainDirectory;
import ovm.core.domain.Oop;
import ovm.core.execution.CoreServicesAccess;
import ovm.services.monitors.Monitor;
import s3.util.PragmaNoInline;
import s3.util.PragmaAtomic;


/**
 * Provide some default behavior for moving garbage collectors that do
 * not support pin and unpin operations.  pin and unpin acquire and
 * release a lock that is also acquired by garbageCollect. This ensures
 * that GC can't commence while an object is pinned.
 * <p>No change is made to the allocation routines, nor to <tt>getMem</tt>
 * so subclass implementations are required to provide atomicity across
 * allocation and GC in a suitable way.
 * The default implementation of {@link #garbageCollect} uses 
 * {@link PragmaAtomic} to ensure atomicity. Subclass implementations should 
 * either make allocation routines do the same, or override 
 * <tt>garbageCollect</tt>.
 *
 * @author <a href=mailto://baker29@cs.purdue.edu> Jason Baker </a>
 **/
public abstract class NonPinningMemoryManager extends MemoryManager {
    // We should really be using read/write locks rather than simple
    // monitors here

    /** Object that is locked upon a {@link #pin} and unlocked upon a
     *  {@link #unpin}. The {@link #garbageCollect method also locks this
     * object whilst doing GC.
     */
    protected static final Object gcCriticalRegionLock = new Object();

    /** The monitor for the lock object, which we have to re-allocate to
        avoid allocation during the synchronized call.
    */
    protected static /*final*/ Monitor gcCriticalRegionMonitor;

    public void fullyBootedVM()  {
	CoreServicesAccess csa =
	    DomainDirectory.getExecutiveDomain().getCoreServicesAccess();
	// Pre-fatten the critical region lock, so that we don't need
	// to allocate a monitor before GC.
	Oop oops = VM_Address.fromObject(gcCriticalRegionLock).asOop();
	gcCriticalRegionMonitor = csa.ensureMonitor(oops);
    }

    /**
     * Only true if the object in question is not in the heap.
     **/
    public boolean canPinEfficiently(Oop oop) {
	return areaOf(oop) != getHeapArea();
    }

    /**
     * Lock out GC if our argument is on the heap.
     **/
    public void pin(Oop oop) {
	if (!canPinEfficiently(oop))
	    gcCriticalRegionMonitor.enter();
    }

    /**
     * Permit GC if our argument is on the heap
     */
    public void unpin(Oop oop) {
	if (!canPinEfficiently(oop))
	    gcCriticalRegionMonitor.exit();
    }

    /**
     * The actual garbage collection implementation.
     * {@link #garbageCollect calls this method after ensuring that no movable
     * objects are pinned.
     **/
    protected abstract void doGC() throws PragmaNoInline;
    
    /**
     * Performs garbage collection. This implementation executes atomically
     * through use of {@link PragmaAtomic}. First it acquires the 
     * {@link #gcCriticalRegionLock} to ensure no moveable objects are
     * pinned, then it invokes {@link #doGC} to perform the actual garbage
     * collection cycle.
     * <p>Subclasses should override to provide atomicity in a different way.
     */
    public void garbageCollect() throws PragmaNoInline, PragmaAtomic {
	GCNative.SAVE_REGISTERS();
	synchronized (gcCriticalRegionLock) {
	    // ppc-linux-gcc-3.2.2 bug: with any level of optimization
	    // (including -O1), you cannot access parameters to a
	    // function that uses j2c's SAVE_REGISTERS().  I've tried
	    // everything I can think of to make this work, including
	    // clobbering each callee-save register individually.
	    ((NonPinningMemoryManager) MemoryManager.the()).doGC(); 
	}
    }
}
