package s3.services.memory.mostlyCopying;

import ovm.core.OVMBase;
import ovm.core.domain.Oop;
import ovm.core.execution.Activation;
import ovm.core.services.memory.VM_Address;
import ovm.core.services.memory.VM_Word;
import s3.util.PragmaNoPollcheck;
import s3.util.PragmaNoInline;
import s3.util.PragmaAtomic;

/**
 * Add support for explicit pinning to the mostly-copying collector.
 * {@link #pin(ovm.core.domain.Oop)} prevents an object reference from
 * being moved during GC, and should eventually be followed by a call
 * to {@link #unpin(ovm.core.domain.Oop)}, which permits normal
 * handling of the reference.  We don't track the actual pointers that
 * have been pinned, but merely the blocks that they reside on.<p>
 *
 * An object should be pinned when C library or kernel code will hold
 * a pointer to or within that object for an extended period of time.
 * For instance, if the contents of a <code>byte[]</code> are passed to
 * <code>aio_write</code>.  The contents of a <code>byte[]</code> may
 * safely be passed to the ordinary <code>write</code> system call
 * because calls to C via
 * {@link ovm.services.bytecode.Instruction.INVOKE_NATIVE} are
 * atomic.<p>
 *
 * In a realtime setting, in which exception handlers run during
 * garbage collection, any reference that may be read or written from
 * an exception handler can <i>[and we may later decide must]</i> be
 * pinned to ensure heap consistency.
 **/
public class PinningManager extends Manager {
    short[] pinnedBlocks;

    public PinningManager(String heapSize, String blockSize, String dump) {
	super(heapSize, blockSize, dump);
	pinnedBlocks = new short[this.heapSize >> blockShift];
    }

    /**
     * Prevents the garbage collector from moving the given object by pinning
     * the page upon which it resides. This method executes atomically,
     * that is without any interruption, but is not safe to call from a thread
     * that has preempted the GC as the pinned block won't be seen by an in
     * progress GC pass.
     */
    public void pin(Oop oop) throws PragmaNoPollcheck {
	VM_Word offset = VM_Address.fromObject(oop).diff(heapBase);
	if (offset.uLT(VM_Word.fromInt(heapSize))) {
	    int idx = offset.asInt() >>> blockShift;
	    pinnedBlocks[idx]++;
	    // check for overflow
	    OVMBase.OVMassert(pinnedBlocks[idx] > 0);
	}
    }

    /**
     * Allows the garbage collector to move the given object (by unpinning
     * its page. It is an error to call this if the object has not previously
     * been pinned. This method executes atomically, that is without any 
     * interruption, but if called from a thread that has preempted the GC, 
     * the unpinned block won't be seen as such by an in progress GC pass -
     * which is not in itself a harmful occurrence.
     */
    public void unpin(Oop oop) throws PragmaNoPollcheck {
	VM_Word offset = VM_Address.fromObject(oop).diff(heapBase);
	if (offset.uLT(VM_Word.fromInt(heapSize))) {
	    int idx = offset.asInt() >>> blockShift;
	    pinnedBlocks[idx]--;
	    OVMBase.OVMassert(pinnedBlocks[idx] >= 0);
	}
    }

    /**
     * Performs garbage collection.
     * This method provides atomicity through use of {@link PragmaAtomic}
     */
    public void garbageCollect() throws PragmaNoInline, PragmaAtomic {
	GCNative.SAVE_REGISTERS();
        // Do we need the gcc bug workaround listed in NonPinningManager?
        doGC();
    }


    public boolean canPinEfficiently(Oop _) {
	return true;
    }

    public void markConservativeRoots(Activation act) {
	// subtle point: block.pin will complain if we try to pin the
	// same block twice.  Be sure to do explicit pinning before
	// the conservative stack walk.
	for (int i = 0; i < pinnedBlocks.length; i++)
	    if (pinnedBlocks[i] > 0)
		block.pin(i);
	super.markConservativeRoots(act);
    }
}
