// $Header: /p/sss/cvs/OpenVM/src/s3/core/services/events/EventManagerImpl.java,v 1.16 2005/03/09 02:49:19 dholmes Exp $

package s3.core.services.events;

import ovm.core.execution.NativeInterface;
import ovm.core.execution.Native;
import ovm.core.stitcher.ThreadServiceConfigurator;
import ovm.core.stitcher.ThreadServicesFactory;
import ovm.core.services.events.EventManager;
import ovm.services.threads.UserLevelThreadManager;
import ovm.util.OVMError;
import ovm.core.services.memory.*;
import s3.util.PragmaAtomic;
import s3.util.PragmaNoPollcheck;

/**
 *
 * @author Filip Pizlo, David Holmes
 */
public class EventManagerImpl
    extends ovm.services.ServiceInstanceImpl
    implements EventManager {
    
    /** Dynamic growth of the eventprocesor set is supported but should be
        avoided in some configurations.
    */
    public static final int PROCESSORS_ARRAY_STEP = 4;
    
    /** the set of registered event processors - initally 4 processors
        are allowed for
    */
    protected EventProcessor[] processors = new EventProcessor[4];
    protected int numProcessors = 0;
    
    private static final class Helper implements NativeInterface {
        static final native void eventsSetEnabled(boolean enabled);
        static final native void waitForEvents();
    }
    
    private final static EventManager instance = new EventManagerImpl();
    
    public static EventManager getInstance() {
        return instance;
    }
    
    /**
     * The current enabled state
     */
    protected boolean enabled = false;

    /**
     * Are we started?
     */
    protected volatile boolean started = false;

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

    /** 
     * Initialisation of the event manager simply involves grabbing
     * a thread manager.
     */
    public void init() {
        isInited = true;
    }

    // warning: DO NOT perform any allocation in this method!!!! Use only
    // raw native I/O
    public boolean setEnabled(boolean enabled) throws PragmaNoPollcheck {
        boolean temp = this.enabled;
        this.enabled = enabled;
        Helper.eventsSetEnabled(enabled);
        return temp;
    }

    public boolean isEnabled() { return enabled; }

    /**
     * Starts the event manager. This actually does nothing as the event
     * manager won't actually do anything until it is enabled, and that
     * must be done explicitly. But you must still call this as part of
     * the service instance protocol.
     * @throws OVMError.IllegalState {@inheritDoc}
     */
    public void start() {
        if (started) {
            throw new OVMError.IllegalState("event manager already started");
        }
        
        Native.makeSignalEventSimple();
        Native.makeSignalEventFromThreadBroken();
        
        started = true;
        d("EventManager has been started");
    }

    public void stop() {
        if (!started) {
            return;
        }
        stopped = true;
        setEnabled(false);
    }

    public boolean isRunning() {
        return started & !stopped;
    }

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

    protected void callEventProcessor(int i)
	throws PragmaNoPollcheck {
	try {
	    processors[i].eventFired();
	}
	catch (StackOverflowError soe) {
	    // we handle this specially because the other debug code will
	    // likely retrigger a stackoverflow
	    Native.print_string("Warning: processEvents - stackoverflow has occurred\n");
	    // normally we don't allow exceptions to propagate from
	    // here but we make a special allowance for a stack
	    // overflow. The fact that the stackoverflow occurred
	    // may well have left the OVM internals in an inconsistent
	    // state. - DH
	    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: processEvents - exception from eventProcessor[ ");
		Native.print_int(i);
		Native.print_string("] = ");
		Native.print_string(processors[i].toString());
		Native.print_string(": ");
		Native.print_string(t.toString());
		Native.print_string("\n");
	    }
	    catch (Throwable t2) {
		Native.print_string("\nWarning: processEvents - secondary exception\n");
	    }
	    finally {
		MemoryPolicy.the().leave(r1);
	    }
	}
    }

    // this is called with rescheduling disabled via the CSA
    public void processEvents() throws PragmaNoPollcheck {
        for (int i = 0; i < numProcessors; ++i) {
	    callEventProcessor(i);
        }
    }
    
    public void waitForEvents() throws PragmaNoPollcheck {
        Helper.waitForEvents();
        processEvents();
    }

    /** The actual implementation for adding an event processor and can be
        overridden by subclasses.
        This is only called with PragmaAtomic active.
    */
    protected int addEventProcessorImpl(EventProcessor handler) 
        throws PragmaNoPollcheck {
	int index=numProcessors;
	if (index == processors.length) {
            Native.print_string("\nWARNING: Growing eventProcessor[] from ");
            Native.print_int(index);
            Native.print_string("\n");
	    EventProcessor[] newArray =
		new EventProcessor[processors.length+PROCESSORS_ARRAY_STEP];
	    System.arraycopy(processors, 0, newArray, 
			     0, processors.length);
	    processors = newArray;
	}
	processors[index] = handler;
	numProcessors++;
	return index;
    }
    
    /**
     * {@inheritDoc}
     * <p>This method executes atomically and establishes the correct
     * allocation context in case <tt>addEventProcessImpl</tt> needs to
     * allocate
     */
    public final void addEventProcessor(EventProcessor handler)
	throws PragmaAtomic {
	VM_Area prev = MemoryManager.the().
            setCurrentArea(MemoryManager.the().getImmortalArea());
	try {
	    addEventProcessorImpl(handler);
	} finally {
	    MemoryManager.the().setCurrentArea(prev);
	}
    }

    protected void removeEventProcessorHook(int i) throws PragmaNoPollcheck {
    }
    
    public final void removeEventProcessor(EventProcessor handler)
	throws PragmaAtomic {
	for (int i = 0; i < processors.length; ++i) {
	    if (processors[i] == handler) {
		removeEventProcessorHook(i);
		
		// fill in the gap
		processors[i] = processors[--numProcessors];
		// don't hold extra references
		processors[numProcessors] = null;
		break;
	    }
	}
    }

    public boolean hasNonTrivialDumpStateHook() {
	return false;
    }

    public void dumpStateHook() {
	// do nothing
    }
    
}

