From: gor@cs.strath.ac.uk (Gordon Russell)
Newsgroups: comp.sys.sequent
Subject: A new approach to Unix Threads
Date: 4 Sep 92 09:28:56 GMT
Organization: Comp. Sci. Dept., Strathclyde Univ., Glasgow, Scotland.


 I have been working on a simple threads package which preferably
would work on the two classes of machines I currently have access to..
sun4s and a sequent symmetry. I have been trying to achieve this
through the use of a context switch during signal handling (triggered
under a setitimer event). However, this does not appear to work
well on the symmetry (but only slightly better on the Sun4s :-) ).
Could someone out there help me by having a quick look at my code?
Sorry to waste general news bandwidth with a program (it is pretty small
though). Even some suggestions would be useful, for I am at the end of my
teather. 

This program is not the best one I have to do this. The best that I have
achieved so far is one which doesn't core dump, but will corrupt the
totals being calculated in the two tasks of the context switch occurred
during a calculation loop. I have a feeling that the signal handler just
does not allow me the access to system registers that I require.
Again, please dont flame me for posting a program..., but feel free
to email me or post with any (helpfull) comments.

PS I know about the threads package which comes in xtank, but it is a
   volenterary (sp?) thread switch, which is unsuitable for my application.
   

/* Compile with -DSYMMETRY */

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>

#define TASK_OFF (1<<(SIGVTALRM-1))
#define TASK_ON  (0)
#define INTERVAL (500000)

#define DISABLE_TASK_SWITCH() sigsetmask(TASK_OFF)
#define ENABLE_TASK_SWITCH()  sigsetmask(TASK_ON)

typedef struct {
  int number_of_tasks;
  struct sigcontext *context;
  int last_task;
} task_structure;

static task_structure TASKINFO;

/*
 * Copy the current context to its save buffer in task_structure, and write in
 * the next context.
 */
void swap_context(newcont,oldcont,context)
int newcont,oldcont;
struct sigcontext *context;
{
 TASKINFO.context[oldcont] = *context;
 *context = TASKINFO.context[newcont]; 
}

/*
 * SIGNAL handler routine, which selects the next thread, swaps to that
 * context, and then returns, thus activating the new thread.
 */
task_control(sig,code,context)
int sig,code;
struct sigcontext *context;
{
struct itimerval timer;
int newcontext_no;

  /* Get the next thread's process id */
  newcontext_no = TASKINFO.last_task+1;
  if (newcontext_no > TASKINFO.number_of_tasks) newcontext_no = 0;

  /* Select the new thread */
  swap_context(newcontext_no,TASKINFO.last_task,context);
  TASKINFO.last_task = newcontext_no;

  /* Set the round-robin scheduler interrupt timer */
  timer.it_value.tv_sec = 0;
  timer.it_value.tv_usec = INTERVAL;
  timer.it_interval.tv_sec = 0;
  timer.it_interval.tv_usec = 0;
  if (setitimer(ITIMER_VIRTUAL,&timer,NULL)) perror();
}

void startup_tasking()
{
struct itimerval timer;
struct sigvec task_struct;
struct sigstack sig;

  /* Initialise the save buffer with enough space to save the MAIN thread */
  TASKINFO.number_of_tasks = 0;
  TASKINFO.context   = (struct sigcontext *)malloc(sizeof(struct sigcontext));
  TASKINFO.last_task = 0;

  /* Create a signal stack for the scheduler to run on */
  sig.ss_sp = (char *)malloc(1024);
  sig.ss_onstack = 0;
  if (sigstack (&sig,NULL) != 0) perror();

  /* Insert the scheduler into the signal handler table */
  task_struct.sv_handler = task_control;
  task_struct.sv_mask    = TASK_OFF;
#ifdef SYMMETRY
  task_struct.sv_onstack = 1;
#endif
  if (sigvec(SIGVTALRM,&task_struct,NULL) != 0) perror;

  /* Start off the timer to activate scheduler */
  timer.it_value.tv_sec = 0;
  timer.it_value.tv_usec = INTERVAL;
  timer.it_interval.tv_sec = 0;
  timer.it_interval.tv_usec = 0;
  if (setitimer(ITIMER_VIRTUAL,&timer,NULL)) perror();
}

/*
 * This identifies a function as a task.
 */
void background_task(task_addr)
void (* task_addr)();
{
char *newstack;
struct sigcontext *cont;
int bank,reg;
  DISABLE_TASK_SWITCH();             /* Dont allow the scheduler to run here */
  newstack = (char *)malloc(4000);   /* Create a new stack for the process */
  TASKINFO.number_of_tasks+=1;       /* Increment the no of threads in system */
  TASKINFO.context = (struct sigcontext *)
           realloc(TASKINFO.context,(TASKINFO.number_of_tasks+1) * sizeof(struct sigcontext));

  /* Set up the initial state of the new thread */
#ifdef SYMMETRY
  cont = TASKINFO.context+TASKINFO.number_of_tasks;
  cont->sc_onstack = 0;              /* sigstack state to restore */
  cont->sc_mask    = TASK_ON ;       /* signal mask to restore */
  cont->sc_sp      = (int)newstack;  /* sp to restore */
  cont->sc_pc      = (int)task_addr; /* pc to restore */
#endif

  ENABLE_TASK_SWITCH();              /* Restart the scheduler now */
}

void task2()
{
int a,b,c;
  c=0;
  do {
    DISABLE_TASK_SWITCH();
    b=0; for (a=1; a<90000; a++) b+=a;
    ENABLE_TASK_SWITCH();
    printf ("Completed 90000 sum %x\n",b);
  }while (1);
}

void task1()
{
int a,b,c;
  c=0;
  do {
    b=0; for (a=1; a<100000; a++) b+=a;
    printf ("Completed 100000 sum %x\n",b);
  }while (1);
}

main()
{
  startup_tasking();
  ENABLE_TASK_SWITCH(); 
  background_task(task2); 
  task1();
}


+--------------------------------+-------------------------------------------+
|  Gordon Russell                |  EMAIL     : gor@cs.strath.ac.uk          |
|  L13.13a, Livingstone Tower,   |  TELEPHONE : 041-552-4400   Ex 3703       |
|  University Of Strathclyde,    |  FAX       : 041-552-0775                 |
|  26 Richmond Street,           +-------------------------------------------+
|  Glasgow, G1 1XH               | Spelling mistakes within this document are|
|  Scotland, UK                  | caused by internet compaction algorithms. |
+--------------------------------+-------------------------------------------+
Newsgroups: comp.sys.sequent