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


#include "NativeProcess.h"
#include "NativeMethod.h"

#include "machine.h"

#include "tramp.h"

#define STABLE_PROCS


size_t		     NativeProcess::defaultStackSize= 4*1024*1024;	// 1Mb stack
List<NativeProcess> *NativeProcess::activeList=	      0;
List<NativeProcess> *NativeProcess::inactiveList=     0;
NativeProcess	    *NativeProcess::activeProcess=    0;


NativeProcess::NativeProcess(void)
  : process(0),
    stackSize(defaultStackSize),
    stackSegment((char *)xmalloc(stackSize)),
    suspendedFrame(0),
    next(0)
{}

NativeProcess::~NativeProcess(void)
{
  assert(stackSegment != 0);
  xfree(stackSegment);
  stackSegment= 0;
}


inline void NativeProcess::recycle(Process *stProc)
{
  printf("TRM [%p %p] -> RECYCLED\n", stProc, stProc->suspendedContext);

  NativeProcess *proc= findActive(stProc);
  if (proc == 0)
    fatal("recycled process not in activeList");
  activeList->remove(proc);
  inactiveList->add(proc);
}


void NativeProcess::reloadAndResumeContext(Context *newCtx)
{
  activeProcess->initContext(newCtx);
  activeProcess->suspendedFrame->resume();
}


void NativeProcess::reloadAndResumeProcess(Process *newProc)
{
  activeProcess->initProcess(newProc);
  activeProcess->suspendedFrame->resume();
}


void NativeProcess::loadAndResumeInitialProcess(Process *newProc)
{
  activeProcess= newProcess(newProc);
  activeProcess->suspendedFrame->resume();
}


Frame *NativeProcess::transferTo(Process *oldProc, Process *newProc)
{
#ifdef STABLE_PROCS

  PRINTF(("XFER %p\n", newProc));

  assert(activeFrame != 0);

  oldProc->pushRemappable();
  newProc->pushRemappable();
  Context *topCtx= activeFrame->stabiliseAll();
  newProc= popRemappable(Process);
  oldProc= popRemappable(Process);

  oldProc->checkStore(oldProc->suspendedContext= topCtx);

  tramp_callOnCStack((void *)reloadAndResumeProcess, (int)newProc, 0, 0);
  // never returns...

  return 0;

#else // !STABLE_PROCS

  assert(activeProcess->process == oldProc);
  assert(activeFrame != 0);

  Frame *frame= activeFrame;

  activeProcess->suspendedFrame= activeFrame;
  if (!frame->hasPseudoContext())
    {
      oldProc->pushRemappable();
      newProc->pushRemappable();
      frame->allocatePseudoContext();
      newProc= popRemappable(Process);
      oldProc= popRemappable(Process);
    }
  oldProc->checkStore(oldProc->suspendedContext= frame->pseudo);

  assert(activeProcess->suspendedFrame->pseudo == oldProc->suspendedContext);

  NativeProcess *proc= findActive(newProc);
  if (proc == 0)
    {
      // no gc: we reuse the stable contexts for pseudos
      proc= newProcess(newProc);
    }

  printf("XFR [%p %p %p] [%p %p %p] ",
	 activeProcess, activeProcess->suspendedFrame, activeProcess->suspendedFrame->pseudo,
	 oldProc, oldProc->suspendedContext, oldProc->suspendedContext->stack[0]);
  oldProc->print();
  putchar('\n');
  printf(" -> [%p %p %p] [%p %p %p] ",
	 proc, proc->suspendedFrame, proc->suspendedFrame->pseudo,
	 newProc, newProc->suspendedContext, newProc->suspendedContext->stack[0]);
  newProc->print();
  putchar('\n');

  // one might call me suspicious...
  assert(proc != 0);
  assert(proc->process == newProc);
  assert(newProc->suspendedContext->isPseudoContext());
  assert(newProc->suspendedContext->asPseudoContext()
	 ->frame() == proc->suspendedFrame);

  assert(proc->suspendedFrame->pseudo == newProc->suspendedContext);

  // make it active
  activeProcess= proc;
  activeFrame= proc->suspendedFrame;

# ifdef RECYCLE_PROCESSES
  // check if old process is dead
  if ((oldProc->suspendedContext->sender == nilObj) &&
      (oldProc->suspendedContext->homeOrReceiver == nilObj))
    {
      recycle(oldProc);
    }
# endif

  return proc->suspendedFrame;

#endif // !STABLE_PROCS
}


void NativeProcess::terminate(Process *stProc)
{
  NativeProcess *proc= findActive(stProc);
  assert(proc != 0);
  activeList->remove(proc);
#     ifdef RECYCLE_PROCESSES
  inactiveList->add(proc);
#     else
  delete proc;
#     endif
}



inline void NativeProcess::mark(void)
{
  PRINTF(("MARKING PROCESS %p\n", this));

  // these are the conditions under which I think a Process is dead...

#ifndef STABLE_PROCS

  if (process->suspendedContext->isNil())
    {
      printf("Process %p has no suspendedContext!\n", this);
    }
  else
    {
      if (((!process->suspendedContext->isPseudoContext())
	   || (process->suspendedContext->asPseudoContext()
	       ->frame() != suspendedFrame))
	  && this != activeProcess)
	{
	  printf("Process %p ", this);
	  process->print();
	  putchar('\n');
	  printf("   has changed suspendedContext\n");
	  printf("   from %p ", suspendedFrame->pseudo);
	  if (suspendedFrame->pseudo)
	    suspendedFrame->pseudo->print();
	  else
	    suspendedFrame->print();
	  putchar('\n');
	  printf("     to %p ", process->suspendedContext);
	  process->suspendedContext->print();
	  putchar('\n');
	}
      if ((process->suspendedContext->homeOrReceiver->isNil())
	  && (process->suspendedContext->sender->isNil()))
	{
	  printf("Process %p has nil receiver and sender!\n", this);
	}
    }

#endif

  suspendedFrame->markStack();

  // the following is conservative: could be removed if we're
  // certain Smalltalk will never drop the last reference to a
  // Process before we terminate the corresponding
  // NativeProcess...
  process->mark();
}


void NativeProcess::markProcesses(void)
{
#ifdef STABLE_PROCS
  activeProcess->mark();
#else
  assert(activeFrame != 0);
  assert(Processor->activeProcess == activeProcess->process);
  activeProcess->suspendedFrame= activeFrame;
  listDo (*activeList, proc)
    proc->mark();
#endif
}


inline void NativeProcess::remap(void)
{
  PRINTF(("REMAPPING PROCESS %p\n", this));
  suspendedFrame->remapStack();
  process= process->remap()->asProcess();
}


void NativeProcess::remapProcesses(void)
{
#ifdef STABLE_PROCS
  activeProcess->remap();
#else
  listDo (*activeList, proc)
    proc->remap();
#endif
}


inline void NativeProcess::stabilise(void)
{
  process->checkStore(process->suspendedContext=
		        suspendedFrame->stabiliseAll());
}


// Stabilise all Process stacks; answer the stable Context
// corresponding to the activeFrame.

Context *NativeProcess::stabiliseAll(void)
{
  assert(activeFrame != 0);
  assert(activeContext->isNil());
# ifdef STABLE_PROCS
  return activeFrame->stabiliseAll();
# else
  activeProcess->suspendedFrame= activeFrame;
  listDo (*activeList, proc)
    proc->stabilise();
  return activeProcess->process->suspendedContext;
# endif
}


void NativeProcess::okayProcessOops(void)
{
# ifdef NDEBUG
  fatal("NDEBUG is defined");
# endif
  process->okayFields();
  suspendedFrame->okayStackOops();
}


void NativeProcess::okayOops(void)
{
# ifdef NDEBUG
  fatal("NDEBUG is defined");
# endif
#ifdef STABLE_PROCS
  activeProcess->okayProcessOops();
#else
  listDo (*activeList, proc)
    proc->okayProcessOops();
#endif
}
