
package s3.core.services.timer;

import ovm.core.execution.NativeInterface;
import ovm.core.services.timer.TimeConversion;
import ovm.core.services.timer.TimerInterruptAction;
import ovm.core.services.timer.TimerManager;
import ovm.core.stitcher.*;
import ovm.core.services.events.EventManager;
import ovm.util.OVMError;
import ovm.core.services.process.ForkManager;
import ovm.core.services.memory.MemoryPolicy;
import ovm.core.execution.Native;
import ovm.core.services.memory.*;

import s3.util.queues.*;
import s3.util.PragmaAtomic;
import s3.util.PragmaNoPollcheck;
import s3.services.threads.TimedSuspensionOVMThreadImpl;

/**
 * A straight-foward and generic implementation of the {@link TimerManager} 
 * interface.
 * <p>This implementation works in conjunction with the {@link EventManager}.
 *
 * <p>The functionality of this class is divided into four groups:
 * <ul>
 * <li>The control functions: {@link #start}, {@link #stop}, 
 * {@link #setTimerInterruptPeriod} etc, which deal with
 * the configuration and general operation of the timer
 * <li>The {@link #eventFired} method 
 * which defines the mechanics of
 * processing the registered actions on each interrupt;
 * <li>The methods for maintaining the list of registered actions 
 * ({@link #addTimerInterruptAction}, {@link #removeTimerInterruptAction},
 * and {@link #getRegisteredActions}; and
 * <li>The methods for inserting, updating and querying the delay queues
 * </ul>
 * <h3>Concurrency Control and Exclusion Requirements</h3>
 * <p>This class is not thread safe in a general sense. Concurrency control
 * is defined in terms of the four groups defined above.
 * <p>The control functions are not generally expected to be thread-safe, as
 * it makes little sense to have threads racing to try and configure and 
 * control the timer.
 * <p>The {@link #eventFired} method can 
 * logically occur in between the execution of any two bytecodes. Consequently,
 * there will be interference between this method and any of the methods that
 * manipulate or use the list of registered actions. The converse is not true,
 * as further interrupts are disabled when 
 * {@link #eventFired} executes and nothing can &quot;interrupt&quot; it.
 * <p>The methods for maintaining the list of actions may be invoked 
 * concurrently and so exclusion should be enforced across their
 * execution. 
 * <p>The delay queue operations are not thread-safe and rely upon external
 * synchronization.
 * <p>When the event manager is in charge, atomicity relies on
 * external controls. To deal with this we simply declare that the action
 * list manipulation methods must be atomic (using <tt>PragmaAtomic</tt>).
 * We could use a concurrent data structure for the action lists to avoid 
 * the need for atomicity (or we could preclude dynamic updates).
 * The <eventFired</tt> method is always
 * executed atomically.
 *
 * @author David Holmes
 *
 */
public class TimerManagerImpl 
    extends ovm.services.ServiceInstanceImpl
    implements TimerManager, EventManager.EventProcessor {

    /** Internal helper class defining the native call interface
     */
    private static final class Helper implements NativeInterface {
        /**
         * Native code for installing the timer interrupt and 
         * configuring the timer.
         */
        static native void configureTimer(long period);

        /**
         * Native code to remove the timer interrupt
         */
        static native void stopTimer();

        /**
         * Returns the current configured timer period in nanoseconds.
         * The native calls that set the timer period don't report any errors
         * if the timer period is too short - they just change it to the 
         * minimum supported value - or otherwise round to a multiple of the
         * minimum value. So we have to read back to know what was actually
         * set.
         * @return the current timer period in nanoseconds
         */
        static native long getPeriod();

        /**
         * Get the address of the interruptCount variable so we can access 
         * it directly
         */
        static native VM_Address getInterruptCountAddress();
    }


    /**
     * Proxy reference to a native code value
     */
    private VM_Address interruptCount;


    /**
     * Last value of interruptCount
     */
    private volatile int lastCount;

    /**
     * This is a singleton class. The instance can be accessed from here
     * or more usually via the service configurator.
     */
    protected static TimerManager instance = new TimerManagerImpl();

    /**
     * Returns the singleton instance of this class
     * @return the singleton instance of this class
     */
    public static TimerManager getInstance() {
        return instance;
    }

    /**
     * Our list of action objects to fire
     */
    protected TimerInterruptAction[] actions;

    /**
     * The index at which to insert the next action
     */
    protected int index = 0;

    // inherit isInited and isStarted

    /**
     * Are we stopped?
     */
    protected volatile boolean isStopped = false;

    /**
     * the current interrupt period in nanoseconds. Default 10ms.
     */
    protected long period = 10L * TimeConversion.NANOS_PER_MILLI;


    /** The event manager */
    protected EventManager em;

    /**
     * The default size of the actions array. We expect this to be small.
     * We envisage one action for a thread manager, perhaps a second if the
     * thread manager uses time preemption, a third for a single monitor
     * timed-wait queue, and a fourth for the IO manager.
     */
    protected static final int DEFAULT_ACTION_LIST_SIZE = 4;


    /** A timer queue for absolute sleeps/waits */
    protected SingleLinkTimerQueue absQ;

    /** Size of absolute delay queue */
    private int absQSize;
    
    /** A timer queue for relative sleeps/waits */
    protected SingleLinkDeltaQueue relQ;

    /** Size of relative delay queue */
    private int relQSize;


    /**
     * Create a timer manager
     * using the default initial action list size.
     *
     *
     */
    public TimerManagerImpl() {
        this(DEFAULT_ACTION_LIST_SIZE);
    }

    /**
     * Create a timer manager 
     * using the given initial action list size.
     * <p>For a given configuration the number of registered actions may be
     * fixed - in fact in this implementation we expect that - so we don't
     * attempt to do anything fancy in terms of maintaining the action
     * list.
     *
     * @param size The initial size of the action list.
     *
     */
    public TimerManagerImpl(int size) {
        if (size <= 0) {
            size = DEFAULT_ACTION_LIST_SIZE;
        }
        actions = new TimerInterruptAction[size];

        absQ = new SingleLinkTimerQueue();
        relQ = new SingleLinkDeltaQueue();
    }

    // we inherit most docComments

    /** Initialisation of the timer manager is trivial, but should be performed
     *  as part of the {@link ovm.services.ServiceInstance service instance}
     *  requirements.
     */
    public void init() {
        em = ((EventServicesFactory) IOServiceConfigurator
              .config
              .getServiceFactory(EventServicesFactory.name))
            .getEventManager();

        interruptCount = Helper.getInterruptCountAddress();
        isInited = true;
    }


    static final boolean DEBUG = false;

    protected void fire(int ticks) {
        if (relQSize > 0) {
            if (DEBUG) Native.print_string("fire: updating relQ\n");
            int i = relQ.update(ticks*period);
            if (i > 0) {
                relQSize -= i;
                if (DEBUG) Native.print_string("fire: update woke on rel queue\n");
            }
            else {
                if (DEBUG) Native.print_string("fire: no wakeup on rel queue\n");
            }
        }

        if (absQSize > 0) {
            if (DEBUG) Native.print_string("fire: updating absQ\n");
            int i = absQ.update();
            if (i > 0) {
                absQSize -= i;
                if (DEBUG) Native.print_string("fire: update woke on abs queue\n");
            }
            else {
                if (DEBUG) Native.print_string("fire: no wakeup on abs queue\n");
            }
        }
    }

    /**
     * @param obj The object to be delayed, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public void delayRelative(DelayableObject obj, long delay) {
        if (ASSERTS_ENABLED) {
            OVMassert(!absQ.contains(((DelayableSingleLinkDeltaElement)obj)) ? 
                      SUCCESS : "obj already in abs delay queue");
            OVMassert(!relQ.contains(((DelayableSingleLinkDeltaElement)obj)) ? 
                      SUCCESS : "obj already in rel rel queue");
            OVMassert(delay > 0 ? SUCCESS : "delay <= 0");
        }
        relQ.insert(((DelayableSingleLinkDeltaElement)obj), delay);
        relQSize++;
    }


    /**
     * @param obj The object to be delayed, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public boolean delayAbsolute(DelayableObject obj, long delay) {
        if (ASSERTS_ENABLED) {
            OVMassert(!absQ.contains(((DelayableSingleLinkDeltaElement)obj)) ? 
                      SUCCESS : "obj already in abs delay queue");
            OVMassert(!relQ.contains(((DelayableSingleLinkDeltaElement)obj)) ? 
                      SUCCESS : "obj already in rel delay queue");
        }
        boolean ret = absQ.insert(((DelayableSingleLinkDeltaElement)obj), delay);
        if (ret) absQSize++;
        return ret;
    }


    /**
     * @param obj The object to be woken up, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public boolean wakeUpRelative(DelayableObject obj) 
        throws PragmaNoPollcheck {
        boolean ret = relQ.remove((DelayableSingleLinkDeltaElement)obj);
        if (ret) relQSize--;
        return ret;
    }

    /**
     * @param obj The object to be woken up, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public boolean wakeUpAbsolute(DelayableObject obj) 
        throws PragmaNoPollcheck {
        boolean ret = absQ.remove((DelayableSingleLinkDeltaElement)obj);
        if (ret) absQSize--;
        return ret;
    }

    /**
     * @param obj The object to be queried, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public boolean isDelayedRelative(DelayableObject obj) 
        throws PragmaNoPollcheck {
        return relQ.contains((DelayableSingleLinkDeltaElement)obj);
    }


    /**
     * @param obj The object to be queried, which is required to
     * implement the {@link DelayableSingleLinkDeltaElement} interface.
     */
    public boolean isDelayedAbsolute(DelayableObject obj) 
        throws PragmaNoPollcheck {
        return absQ.contains((DelayableSingleLinkDeltaElement)obj);
    }


    // This is the entry point when the event manager is in charge
    public void eventFired() throws PragmaNoPollcheck {

        int currentCount = interruptCount.getInt(); /* latch value */
        int ticks;

        /* this subtraction yields the correct value even when currentCount
           overflows and becomes negative. Hence there is no overflow problem
           with any of this code.
        */
        ticks = currentCount - lastCount;
        lastCount = currentCount;

//         if (ticks > 1 || ticks < 0) {
//             Native.print_string("Timer manager: eventFired with ticks = ");
//             Native.print_int(ticks);
//             Native.print_string("\n");
//         }


        if (ticks==0) {
            return;
        }

        this.fire(ticks);

        processActions(ticks);
    }

    void processActions(int ticks) {
        for (int i = 0; i < index; i++) {
	    processAction(ticks,i);
        }
    }

    public String eventProcessorShortName() {
	return "timer";
    }

    protected void processAction(int ticks,
				 int i) throws PragmaNoPollcheck {
	try {
	    actions[i].fire(ticks);
	}
	catch (StackOverflowError soe) {
	    // we handle this specially because the other debug code will
	    // likely retrigger a stackoverflow
	    Native.print_string("Warning: timerManager.processActions - stackoverflow has occurred\n");
	    throw soe;
	}
	catch (Throwable t) {
	    // This is debug code but watch for generating secondary
	    // exceptions due to memory problems
	    Object r1 = MemoryPolicy.the().enterExceptionSafeArea();
	    try {
		// Should log somewhere !!!
		Native.print_string("Warning: - Exception in timerInterruptAction: ");
		Native.print_string(t.toString());
		Native.print_string("\n");
	    }
	    catch (Throwable t2) {
		Native.print_string("\nWarning: - secondary exception\n");
	    }
	    finally {
		MemoryPolicy.the().leave(r1);
	    }
	}
    }

    /**
     * Adds the specified {@link TimerInterruptAction} to the list of
     * actions to be fired when the timer interrupt occurs. Actions are
     * fired in the order in which this method is invoked.
     */
    public void addTimerInterruptAction(TimerInterruptAction tia)
	throws PragmaAtomic {
	VM_Area prev=
	    MemoryManager.the()
	    .setCurrentArea(MemoryManager.the().getImmortalArea());
	try {
	    addTimerInterruptActionImpl(tia);
	} finally {
	    MemoryManager.the().setCurrentArea(prev);
	}
    }

    protected void addTimerInterruptActionImpl(TimerInterruptAction tia)
	throws PragmaAtomic {
        if (index > actions.length-1) {
            growList();
        }
        actions[index++] = tia;
    }


    /**
     * Increases the size of the action array when it is full
     * 
     */
    protected void growList() {
        TimerInterruptAction[] temp = new TimerInterruptAction[actions.length*2];
        for (int i = 0, j = 0; i < actions.length && actions[i] != null; i++) {
            temp[j++] = actions[i];
        }
        actions = temp;
    }

    // profiling method?
    protected void removeTimerInterruptActionHook(int i) {
    }

    public void removeTimerInterruptAction(TimerInterruptAction tia)
	throws PragmaAtomic {
        for (int i = 0; i < index; i++) {
            if (actions[i] == tia) {
		removeTimerInterruptActionHook(i);
		actions[i]=actions[--index];
		actions[index]=null;
                break;
            }
        }
    }

    public TimerInterruptAction[] getRegisteredActions()
	throws PragmaAtomic {
        TimerInterruptAction[] temp = new TimerInterruptAction[index];
        for (int i = 0; i < index-1; i++) {
            temp[i] = actions[i];
        }
        return temp;
    }


    public long getTimerInterruptPeriod() {
        return period;
    }


    /**
     * Requests that the timer interrupt period be set to the given value.
     * The interrupt period may only be set before the timer manager is
     * started.
     * <p>The actual timer interrupt period may vary from that requested
     * due to the limitations of the underlying timer hardware. You should
     * always check the actual timer period using 
     * {@link #getTimerInterruptPeriod}, after the timer manager has been 
     * started.
     *
     * <p>This method is not thread-safe. Only one thread should control the
     * timer manager's behaviour.
     *
     * @param period the new timer interrupt period in nanoseconds
     * @return <code>true</code> if the period was changed and 
     * <code>false</code> otherwise.
     * @throws OVMError.IllegalState if the timer manager has been started
     * @see #getTimerInterruptPeriod
     *
     */
    public boolean setTimerInterruptPeriod(long period) {
        if (isStarted) {
            throw new OVMError.IllegalState("timer manager already started");
        }
        if (period <= 0 ) {  // OR not suitable in some other way
            return false;
        }
        else {
            this.period = period;
            return true;
        }
    }


    /**
     * Queries if this <code>TimerManager</code> supports the setting of
     * the interrupt period.
     *
     * @return <code>true</code> if the timer manager is not yet started, else
     * <code>false</code>
     *
     *
     * @see #setTimerInterruptPeriod
     */
    public boolean canSetTimerInterruptPeriod() {
        return !isStarted;
    }

    private ForkManager.AfterHandler afterForkHandler =
        new ForkManager.AfterHandler() {
            public void afterInChild() {
                // need to re-initialize the timer since fork() clears timers in the child.
                Helper.configureTimer(period);
            }
            public void afterInParent(int pid) {
                // nothing to do since parent's state is unchanged.
            }
        };

    /**
     * Start the operation of the timer manager. This installs the necessary
     * low-level timer signal handler and configures the system timer to use
     * the preferred interrupt period.
     * <p>It is an error to try to start a timer manager that has been started.
     * Restarting of this timer is not supported.
     * <p>This method is not thread-safe. Only one thread should control and
     * configure the timer.
     *
     * @see #stop
     * @see #canRestart
     * @see #isRunning
     * @throws OVMError.IllegalState if the timer manager has already been
     * started.
     */
    public void start() {
        if (isStarted) {
            throw new OVMError.IllegalState("timer manager already started");
        }
        Helper.configureTimer(period);
        period = Helper.getPeriod(); // read back in case it need rounding
        
        ForkManager.addAfter(afterForkHandler);
        
        isStarted = true;
        em.addEventProcessor(this);
        
        d("Timer has been started with period: " + (period/1000) + " us");

        // note: we're not enabled. That job belongs to whoever is in charge
        // of bootstrapping the system.
    }


    public void stop() {
        if (!isStarted) {
            return;
        }
        ForkManager.removeAfter(afterForkHandler);
        Helper.stopTimer();
        em.removeEventProcessor(this);
        isStopped = true;
    }

    public boolean isRunning() {
        return isStarted && !isStopped;
    }

    /**
     * @throws OVMError.IllegalState if the timer has not been stopped
     */
    public void destroy() {
        if (isRunning()) {
            throw new OVMError.IllegalState("must stop timer first");
        }
    }

    /**
     * Always returns <code>false</code> as restart is not supported.
     *
     * @return <code>false</code> 
     *
     * @see #start
     * @see #stop
     */
    public boolean canRestart() { return false; }

}









