Posted to tcl by GPS at Sun Sep 09 08:37:34 GMT 2007view pretty

/* Tcor by George Peter Staplin */

#include <tcl.h>
#include <stdlib.h>
#include <ucontext.h>

#define OBJ_CMD_ARGS ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]

#define defcmd(func,name) \
 Tcl_CreateObjCommand (interp, name, func, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL)

#define STACK_SIZE 8192

struct lwt {
 ucontext_t context;
 struct lwt *previous, *next;
 void *ptr;
};

static struct lwt *lwts = NULL;
static struct lwt *lwt_current = NULL;
static int lwt_total = 0;

static void lwt_exit (void);
static void lwt_init (void);
static void lwt_spawn (void (*func) (void), void *ptr);
static void lwt_yield (void);
static int tcor_spawn (OBJ_CMD_ARGS);
static int tcor_yield (OBJ_CMD_ARGS);

int Tcor_Init (Tcl_Interp *interp);

static void
lwt_exit (void) {
 struct lwt *l = lwt_current;
 struct lwt *n;

 /* delink the lwt */ 
 if (l->previous) {
  l->previous = l->next;
  if (l->next) {
   l->next->previous = l->previous;
  }
 } else {
  lwts = l->next;
  if (lwts) {
   lwts->previous = NULL; 
  }
 }

 free (l->context.uc_stack.ss_sp);
 free (l);

 n = lwts;
 lwt_current = n;

 if (setcontext (&n->context)) {
  perror ("lwt_exit :- setcontext");
  abort ();
 } 
 /* NOTREACHED */
}

static void
lwt_init ( void ) {
 struct lwt *l;

 l = malloc (sizeof *l);
 if (NULL == l) {
  perror ("lwt_init :- malloc");
  abort ();
 }

 l->ptr = NULL;
 l->previous = NULL;
 l->next = lwts;
 lwts = l;

 lwt_current = l;
 ++lwt_total;
 
 if (getcontext (&l->context)) {
  perror ("lwt_init :- getcontext");
  abort ();
 }

 l->context.uc_link = NULL;
}


static void
lwt_spawn ( void (*func) (void), void *ptr) {
 struct lwt *l;

 l = malloc (sizeof *l);
 if (NULL == l) {
  perror ("lwt_spawn :- malloc lwt");
  abort ();
 }

 l->ptr = ptr;
 l->previous = NULL;
 l->next = lwts;
 lwts = l;

 if (getcontext (&l->context)) {
  perror ("getcontext");
  abort ();
 }

 l->context.uc_link = NULL;
 l->context.uc_stack.ss_sp = malloc (STACK_SIZE);
 if (NULL == l->context.uc_stack.ss_sp) {
  perror ("lwt_spawn :- malloc stack");
  abort ();
 }
 l->context.uc_stack.ss_flags = 0;
 l->context.uc_stack.ss_size = STACK_SIZE;

 makecontext (&l->context, func, 0);
 ++lwt_total;
}

static void
lwt_yield ( void ) {
 struct lwt *l = lwt_current;
 struct lwt *n;

 n = l->next;
 if (NULL == n) {
  n = lwts;
 }

 lwt_current = n;
 
 if (l == n) {
  /* There is only 1 lwt that exists. */
  if (getcontext (&l->context)) {
   perror ("lwt_yield :- getcontext"); 
   abort ();
  }
  return;
 }
  
 if (swapcontext (&l->context, &n->context)) {
  perror ("lwt_yield :- swapcontext");
  abort ();
 }
 /* NOTREACHED */
}

static void
tcor_spawn_start (void) {
 Tcl_Obj *cmd = lwt_current->ptr;
 Tcl_Interp *interp;

 interp = Tcl_CreateInterp ();
 defcmd (tcor_spawn, "tcor-spawn");
 defcmd (tcor_yield, "tcor-yield");

 if (TCL_ERROR == Tcl_EvalObjEx (interp, cmd, TCL_EVAL_GLOBAL | TCL_EVAL_DIRECT)) {
  fprintf (stderr, "spawned task evaluation error: %s\n", Tcl_GetStringResult (interp));
 }
 Tcl_DecrRefCount (cmd);
 Tcl_DeleteInterp (interp);
 lwt_exit ();
} 

static int
tcor_spawn (OBJ_CMD_ARGS) {
 Tcl_Obj *cmd;
 
 if (2 != objc) {
  Tcl_WrongNumArgs (interp, 1, objv, "command-list");
  return TCL_ERROR;
 }
 
 cmd = Tcl_DuplicateObj (objv[1]);
 Tcl_IncrRefCount (cmd);
 lwt_spawn (tcor_spawn_start, cmd);
 
 return TCL_OK;
}

static int
tcor_yield (OBJ_CMD_ARGS) {
 lwt_yield (); 
 return TCL_OK;
}

int Tcor_Init (Tcl_Interp *interp) {
 if (NULL == Tcl_InitStubs (interp, TCL_VERSION, 0))
  return TCL_ERROR;

 defcmd (tcor_spawn, "tcor-spawn");
 defcmd (tcor_yield, "tcor-yield");

 lwt_init ();

 return TCL_OK;
}