// NativeProcess.h -- processes running in native stacks
//
// Author: Ian.Piumarta@INRIA.Fr
//
// Last edited: 2000-12-06 13:47:54 by piumarta on emilia.rd.wdi.disney.com


#include "Object.h"
#include "List.h"
#include "debug.h"

#include "Frame.h"

#define RECYCLE_PROCESSES


class NativeProcess : public ListLink
{
public:

  static size_t		      defaultStackSize;
  static List<NativeProcess> *activeList;
  static List<NativeProcess> *inactiveList;
  static NativeProcess	     *activeProcess;

public:

  Process	*process;		// my associated Smalltalk Process
  size_t	 stackSize;		// number of bytes in my stack segment
  char		*stackSegment;		// lowest address in my stack segment
  Frame		*suspendedFrame;	// native Frame suspended in this Process
  NativeProcess	*next;			// NativeProcess list

public:

  static void initialise(void)
    {
      assert(activeList == 0);
      assert(inactiveList == 0);
      activeList= new List<NativeProcess>;
      inactiveList= new List<NativeProcess>;
    }

  static void release(void)
    {
      if (activeList   != 0) { delete activeList;    activeList= 0; }
      if (inactiveList != 0) { delete inactiveList;  inactiveList= 0; }
    }

protected:
  friend class List<NativeProcess>;

  NativeProcess(void);
  ~NativeProcess(void);

  inline static NativeProcess *allocate(void)
    {
      return
#	ifdef RECYCLE_PROCESSES
	(!inactiveList->isEmpty())
	  ? inactiveList->removeFirst()
	  :
#	endif
	    new NativeProcess();
    }

  inline NativeProcess *initContext(Context *ctx)
    {
      suspendedFrame= Frame::loadStack(ctx, stackSegment, stackSize);
      return this;
    }

  inline NativeProcess *initProcess(Process *stProc)
    {
      process= stProc;
      initContext(stProc->suspendedContext);
      PRINTF(("NEW [%p %p %p] [%p %p %p] ",
	     this, suspendedFrame, suspendedFrame->pcx,
	     stProc, stProc->suspendedContext, stProc->suspendedContext->stack[0]));
      PRINTLN(stProc);
      return this;
    }

  inline static NativeProcess *findActive(Process *stProc)
    {
      listDo (*activeList, elt)
	{
	  if (elt->process == stProc)
	    return elt;
	}
      return 0;
    }

  static void recycle(Process *stProc);

public:

  inline static NativeProcess *newProcess(Process *stProc)
    {
      return activeList->add(allocate()->initProcess(stProc));
    }

  inline Frame *reload(Context *cx)
    {
      return suspendedFrame= Frame::loadStack(cx, stackSegment, stackSize);
    }

public:
  
  static Frame *transferTo(Process *oldProc, Process *newProc);

  static void terminate(Process *stProc);

  static void reloadAndResumeContext(Context *ctx);
  static void reloadAndResumeProcess(Process *stProc);
  static void loadAndResumeInitialProcess(Process *stProc);

  void stabilise(void);
  static Context *stabiliseAll(void);

private:

  // NOTE: If we wanted to keep memory overheads to a strict minimum,
  // we could use GC as an opportunity to delete the contents of the
  // inactiveList.  (Currently we always keep stale NativeProcs for
  // recycling.  We could also delete them immediately on termination.
  // The "optimal" choice depends on whether the local implementation
  // of malloc()+free() is faster than the recycling scheme.  Under
  // GNU it would almost certainly be better NOT to recycle, since the
  // implementations of malloc()+free() are AMAZINGLY fast and tend to
  // return the same area of memory for alternating allocations and
  // deallocations of blocks of the same size.)

  inline void mark(void);
  inline void remap(void);

public:

  static void markProcesses(void);
  static void remapProcesses(void);

  void okayProcessOops(void);
  static void okayOops(void);
};
