Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/runtime/Kernel/cdata.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "mk.h"
/* #include "runtime.h"	-- now included from cinterface.h below */
#include "stableptr.h"	/* MW 19991213, needed for Haskell finalizers */
#include "cinterface.h"	/* MW 19991213, needed for Haskell finalizers */
extern void deferGC (StablePtr finalise);	/* prototype (ditto) */

#define MAX_FOREIGNOBJ 1024

static ForeignObj foreign[MAX_FOREIGNOBJ];

/*static FileDesc fd_stdin;*/
/*static FileDesc fd_stdout;*/
/*static FileDesc fd_stderr;*/
ForeignObj fo_stdin;
ForeignObj fo_stdout;
ForeignObj fo_stderr;

/* The following are functions *not* visible to the Haskell world. */

void initForeignObjs(void)
{
  int i;
  FileDesc *fd_stdin, *fd_stdout, *fd_stderr;

  for(i=0; i<MAX_FOREIGNOBJ; i++) {
    foreign[i].used = 0;
    foreign[i].cval = NULL;
    foreign[i].gc   = NULL;
    foreign[i].gcf  = NULL;
  }
  fd_stdin = (FileDesc*)malloc(sizeof(FileDesc));
  fd_stdin->fp = stdin; 
  fd_stdin->bm = _IOLBF; 
  fd_stdin->size = -1; 
  fd_stdin->path = strdup("<stdin>"); 
    fo_stdin.used = 1; 
    fo_stdin.cval = (void*)fd_stdin; 
    fo_stdin.gcf  = gcNone;
  fd_stdout = (FileDesc*)malloc(sizeof(FileDesc));
  fd_stdout->fp = stdout;
  fd_stdout->bm = _IOLBF;
  fd_stdout->size = -1;
  fd_stdout->path = strdup("<stdout>"); 
    fo_stdout.used = 1; 
    fo_stdout.cval = (void*)fd_stdout;
    fo_stdout.gcf  = gcNone;
  fd_stderr = (FileDesc*)malloc(sizeof(FileDesc));
  fd_stderr->fp = stderr;
  fd_stderr->bm = _IOLBF;
  fd_stderr->size = -1;
  fd_stderr->path = strdup("<stderr>"); 
    fo_stderr.used = 1; 
    fo_stderr.cval = (void*)fd_stderr;
    fo_stderr.gcf  = gcNone;
}

ForeignObj* allocForeignObj(void* arg, gcCval finalCV, gcFO finalFO)
{
  int i;
  for(i=0; i<MAX_FOREIGNOBJ; i++) {
    if(!foreign[i].used) {
      foreign[i].used = 1;
      foreign[i].cval = arg;
      foreign[i].gc   = finalCV;
      foreign[i].gcf  = finalFO;
      /*fprintf(stderr,"allocForeignObj: allocated %d (0x%x) (gcCval %x, gcFO %x)\n",i,&foreign[i],finalCV,finalFO);*/
      return &foreign[i];
    }
  }
  fprintf(stderr,"Error: allocation limit (%d) exceeded for Foreign(Obj/Ptr)\n"
         ,MAX_FOREIGNOBJ);
  return 0;
}

void freeForeignObj(ForeignObj *cd)
{
  /*fprintf(stderr,"freeForeignObj: releasing %d\n",((int)cd-(int)foreign)/sizeof(ForeignObj));*/
  if (cd->gcf)
    cd->gcf(cd);
  else
    fprintf(stderr,"Warning: freeForeignObj called on already-free ForeignObj");
  cd->used = 0;
  cd->gcf  = NULL;
}

void *derefForeignObj(ForeignObj *cd)
{
  /*fprintf(stderr,"derefForeignObj: fo=0x%x cval=0x%x\n",cd,cd->cval);*/
  return cd->cval;
}

void clearForeignObjs(void)
{
  int i;
  /*fprintf(stderr,"clearForeignObjs\n");*/
  for(i=0; i<MAX_FOREIGNOBJ; i++)
    foreign[i].used = 0;
}

void markForeignObj(ForeignObj *cd)
{
  /*fprintf(stderr,"markForeignObj 0x%x used=%d\n",cd,cd->used);*/
  cd->used++;
}

void gcForeignObjs(void)
{
  int i;
  for(i=0; i<MAX_FOREIGNOBJ; i++)
    /*if(foreign[i].used == 0) {
        printf("gcForeignObjs: could reclaim %d (gcFO %x)\n",i,foreign[i].gcf);
      } */
    if(foreign[i].used == 0 && foreign[i].gcf) {
    /*fprintf(stderr,"gcForeignObjs: reclaiming %d (0x%x)\n",i,&foreign[i]);*/
      foreign[i].gcf(&foreign[i]);  /* Call first-stage garbage collector */
      foreign[i].gcf  = NULL;
    } 
}

void gcNow(ForeignObj *cd)	/* This is a possible first-stage GC */
{
  if (cd->gc)
    cd->gc(cd->cval);		/* Call the second-stage garbage collector */
  cd->cval = NULL;		/* and ensure we don't keep dead values */
  cd->gc   = NULL;
}
void gcLater(ForeignObj *cd)	/* This is another possible first-stage GC */
{
  if (cd->gc)
    deferGC(cd->gc);		/* Call the second-stage garbage collector */
  cd->cval = NULL;		/* and ensure we don't keep dead values */
  cd->gc  = NULL;
}
void gcNone(ForeignObj *cd)	/* This is another possible first-stage GC */
{
  cd->cval = NULL;		/* Just ensure we don't keep dead values */
  cd->gc   = NULL;
}

void gcFile(void *c)	/* This is a possible second-stage GC */
{
  FileDesc *a = (FileDesc*)c;
#ifdef PROFILE
  if(!replay)
#endif
    if (a->fp)			/* FILE* might have been hClose'd */
      fclose(a->fp);
  /*if (a->path) free(a->path);*/
  /*free(a);	-- free'ing causes a seg-fault! don't know why */
}
void gcSocket(void *c)	/* This is another possible second-stage GC */
{
  FileDesc *a = (FileDesc*)c;
  close(a->fdesc);
  /*if (a->path) free(a->path);*/
  /*free(a);	-- free'ing causes a seg-fault! don't know why */
}


/* ********************* */
/* ForeignObj/Addr stuff */
/* ********************* */

#if 0
/* The following function *is* visible to the Haskell world. */
/* makeForeignObj primitive 2 :: Addr -> () -> IO ForeignObj */
/* -- Note, we assume that the finaliser already has `unsafePerformIO' */
/*    wrapped around it, so its type is really (). */
C_HEADER(primForeignObj)
{
  NodePtr nodeptr, finalise;
  void *addr;
  ForeignObj *fo;
  nodeptr = C_GETARG1(1);
  IND_REMOVE(nodeptr);
  addr = (void*)GET_INT_VALUE(nodeptr);
  nodeptr = C_GETARG1(2);
  IND_REMOVE(nodeptr);
  finalise = nodeptr;

  fo = allocForeignObj(addr, (gcCval)nhc_mkStablePtr(finalise), gcLater);
  nodeptr = (NodePtr)nhc_mkRight(nhc_mkCInt((Int)fo));
  C_RETURN(nodeptr);
}
#endif

/* The following FFI function *is* visible to the Haskell world.         */
/* -- Note, we assume that the finaliser already has `unsafePerformIO'   */
/*      wrapped around it, so its type is really (), not even IO ().     */
/*      Furthermore, it is wrapped in a box so that the primitive call   */
/*      (which evaluates all args to WHNF) does not execute it straight  */
/*      away!                                                            */
/* -- Note also that, normally one *cannot* return a ForeignObj complete */
/*      to the Haskell world.  nhc98 does allow it, but this is the only */
/*      occasion where it actually makes sense.                          */

/* foreign import makeForeignObjC :: Addr -> _E a -> IO ForeignObj       */
void *primForeignObjC (void *addr, NodePtr fbox)
{
  ForeignObj *fo;
  NodePtr finalise;
  finalise = GET_POINTER_ARG1(fbox,1);
  fo = allocForeignObj(addr, (gcCval)makeStablePtr(finalise), gcLater);
/*fprintf(stderr,"primForeignObjC: addr=0x%x finaliser=0x%x\n",addr,finalise);*/
  return nhc_mkCInt((int)fo);
}

/* foreign import newForeignPtr
                  :: FinalizerPtr () -> Ptr a -> IO (ForeignPtr a) */
void *primForeignPtrC (gcCval finaliser, void *addr)
{
  ForeignObj *fo;
  fo = allocForeignObj(addr, finaliser, gcNow);
  /*fprintf(stderr,"primForeignObjC: addr=0x%x finaliser=0x%x fo=0x%x\n",addr,finaliser,fo);*/
  return nhc_mkCInt((int)fo);
}

#if 0
/* 'addrToHandle' is very tricky!  The Addr *must* be a pointer to   */
/* a ForeignObj that has already been allocated in C-land.  This     */
/* function just changes the types around for Haskell-land.          */
void *
addrToHandle (void* addr)
{
  fprintf(stderr,"addrToHandle: addr=0x%x\n",addr);
  return nhc_mkCInt((int)addr);
}
#endif

/* The following function is also visible to the Haskell world.       */
/* It _must_ be a primitive, not a foreign import, because the latter */
/* mechanism would dereference the ForeignObj argument to become just */
/* an Addr, which is the opposite of what we want here.               */

/* reallyFreeForeignObj primitive 1 :: ForeignObj -> IO ()            */
C_HEADER(reallyFreeForeignObj)
{
  NodePtr nodeptr;
  ForeignObj *fo;
  nodeptr = C_GETARG1(1);
  IND_REMOVE(nodeptr);
  fo = (void*)GET_INT_VALUE(nodeptr);

  /*fprintf(stderr,"reallyFreeForeignObj: releasing %d (0x%x)\n",((int)fo-(int)foreign)/sizeof(ForeignObj),fo);*/
  freeForeignObj(fo);
  C_RETURN(nhc_mkUnit());
}

static StablePtr pending[MAX_FOREIGNOBJ];  /* queue for pending finalisers */
       int       pendingIdx=0;

void deferGC (StablePtr finalise)
{
  if (++pendingIdx >= MAX_FOREIGNOBJ) {
    fprintf(stderr,"Warning: mismatch in limits for ForeignObjs and finalisers.\n");
    exit(1);
  }
  pending[pendingIdx] = finalise;	/* put finaliser in the queue	*/
}

void runDeferredGCs (void)
{
  static int alreadyRunning=0;	/* need lock in case a finaliser triggers GC! */

  if (alreadyRunning) {
    /*fprintf(stderr,"Warning: running ForeignObj finalisers has triggered another GC!\n");*/
    return;
  } else alreadyRunning=1;	/* grab mutex lock before entering */

  {
    int i;
    NodePtr n;
    CodePtr IP=Ip;		/* save global instruction pointer */
    NodePtr *SP=Sp;		/*                   stack pointer */
    NodePtr *FP=Fp;		/*                   frame pointer */

    /*fprintf(stderr,"runDeferredGCs: %d finalisers to process\n",pendingIdx);*/

    for (i=1; i<=pendingIdx; i++) {/* traverse the queue */
      n = derefStablePtr(pending[i]);
      C_PUSH(n);
      C_EVALTOS(n); 		/* .. run each finaliser, discarding result */
      C_POP();
      freeStablePtr(pending[i]);/* .. then permit GC of finaliser itself */
      pending[i] = NULL;
    }
    Ip=IP;			/* restore global instruction pointer */
    Sp=SP;			/*                      stack pointer */
    Fp=FP;			/*                      frame pointer */
    pendingIdx = 0;		/* finally, reset the queue */
  }
  alreadyRunning = 0;		/* and release the mutex lock */
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.