#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  clock.c kbd.c makefile makefile.std parts.c parts.h
#   prog.sty threads.c threads.h watch.c
# Wrapped by mike@client25.comlab on Thu Jun 11 12:51:46 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f clock.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"clock.c\"
else
echo shar: Extracting \"clock.c\" \(2393 characters\)
sed "s/^X//" >clock.c <<'END_OF_clock.c'
X/* clock -- a dual-time clock */
X
X#include "threads.h"
X#include "parts.h"
X
X/* Processes */
XPRIVATE int keyboard, update[2], display, convert, sync;
X
X/* Message types */
X#define ADJUST  1
X#define SWITCH  2
X#define GETTIME 3
X#define TIME    4
X#define INIT    5
X
XPRIVATE void do_keyboard()
X{
X     message m;
X
X     conn_kbd();
X
X     while (TRUE) {
X	  receive(HARDWARE, &m);
X	  switch (m.m_char) {
X	  case '.':
X	       co_exit();
X	  case 'w':
X	       send_int(update[1], ADJUST, -SECSperHOUR);
X	       break;
X	  case 'e':
X	       send_int(update[1], ADJUST, SECSperHOUR);
X	       break;
X	  case 's':
X	       send_int(update[0], SWITCH, 0);
X	       send_int(update[1], SWITCH, 0);
X	       break;
X	  }
X     }
X}
X
XPRIVATE char *text[2] = { "HOME", "AWAY" };
XPRIVATE int legend[2] = { UPDATE0, UPDATE2 };
XPRIVATE int digits[2] = { UPDATE1, UPDATE3 };
X
XPRIVATE void do_update()
X{
X     int kind, window;
X     int offset = 0;
X     message m;
X
X     receive(sync, &m);
X     kind = window = m.m_int;
X     send_string(display, legend[window], text[kind]);
X
X     while (TRUE) {
X#ifdef BAD
X	  char buf[10];
X	  get_time(offset, buf);
X	  send_string(display, digits[window], buf);
X#else
X	  send_int(convert, GETTIME, offset);
X	  receive(convert, &m);
X	  send_string(display, digits[window], m.m_string);
X#endif
X
X	  receive(ANY, &m);
X	  switch (m.m_type) {
X	  case TICK:
X	       break;
X
X	  case ADJUST:
X	       offset += m.m_int;
X	       break;
X
X	  case SWITCH:
X	       window = 1 - window;
X	       send_string(display, legend[window], text[kind]);
X	       break;
X	  }
X     }
X}
X
XPRIVATE void do_convert()
X{
X     message m;
X     char buf[10];
X
X     while (TRUE) {
X	  receive(ANY, &m);
X	  get_time(m.m_int, buf);
X	  send_string(m.m_source, TIME, buf);
X     }
X}
X
XPRIVATE void do_sync()
X{
X     message m;
X
X     conn_tick();
X     send_int(update[0], INIT, 0);
X     send_int(update[1], INIT, 1);
X     while (TRUE) {
X	  receive(HARDWARE, &m);
X	  send_int(update[0], TICK, 0);
X	  send_int(update[1], TICK, 0);
X     }
X}
X
XPUBLIC int main()
X{
X     keyboard  = co_call("keyboard", do_keyboard);
X     update[0] = co_call("update0",  do_update);
X     update[1] = co_call("update1",  do_update);
X     display   = co_call("display",  do_display);
X     convert   = co_call("convert",  do_convert);
X     sync      = co_call("sync",     do_sync);
X     co_start();
X     printf("\n\n");		   /* tidy up the display */
X     return (0);
X}
END_OF_clock.c
if test 2393 -ne `wc -c <clock.c`; then
    echo shar: \"clock.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f kbd.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"kbd.c\"
else
echo shar: Extracting \"kbd.c\" \(967 characters\)
sed "s/^X//" >kbd.c <<'END_OF_kbd.c'
X/* kbd -- copy keyboard characters into a pipe */
X
X#include <sys/types.h>
X#include <signal.h>
X#include <sgtty.h>
X
X#define ON 1
X#define OFF 0
X
X/* cbreak -- set or reset terminal's ECHO and CBREAK */
Xcbreak(fd, flag)
X{
X     static struct sgttyb arg;
X     static short saved_flags;
X
X     if (gtty(fd, &arg) < 0) {
X	  perror("gtty");
X	  exit(1);
X     }
X     
X     if (flag == OFF)
X	  arg.sg_flags = saved_flags;
X     else {
X	  saved_flags = arg.sg_flags;
X	  arg.sg_flags = arg.sg_flags & ~ECHO | CBREAK;
X     }
X
X     if (stty(fd, &arg) < 0) {
X	  perror("stty");
X	  exit(1);
X     }
X}
X
Xhandler(sig)
Xint sig;
X{
X     cbreak(0, OFF);
X     exit(0);
X}
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X     int client = getppid();
X     char buf[1];
X
X     signal(SIGHUP, handler);
X     signal(SIGINT, handler);
X     signal(SIGQUIT, handler);
X     signal(SIGPIPE, handler);
X     cbreak(0, ON);
X
X     for (;;) {
X	  read(0, buf, 1);
X	  write(1, buf, 1);
X	  kill(client, SIGUSR1);
X     }
X}
END_OF_kbd.c
if test 967 -ne `wc -c <kbd.c`; then
    echo shar: \"kbd.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"makefile\"
else
echo shar: Extracting \"makefile\" \(356 characters\)
sed "s/^X//" >makefile <<'END_OF_makefile'
X# Preparation of Practical 1 -- world travel clock
X
XBINDIR = /usr/local/opsys/bin
X
Xall: threads.o parts.o kbd
X
Xthreads.o: threads.h
X
Xparts.o: threads.h parts.h parts.c
X	cc -c $(CFLAGS) -DKBD=\"$(BINDIR)/kbd\" parts.c
X
Xkbd: kbd.c
X	cc $(CFLAGS) -o kbd kbd.c
X
XCLOCK = clock.o threads.o parts.o
Xclock: $(CLOCK)
X	cc -o clock $(CLOCK)
Xclock.o: threads.h parts.h
END_OF_makefile
if test 356 -ne `wc -c <makefile`; then
    echo shar: \"makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f makefile.std -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"makefile.std\"
else
echo shar: Extracting \"makefile.std\" \(248 characters\)
sed "s/^X//" >makefile.std <<'END_OF_makefile.std'
X# Practical 1 -- world travel clock
X
XSRC = /usr/local/opsys/prac1
XCFLAGS = -I$(SRC)
X
Xwatch: watch.o
X	$(CC) $(CFLAGS) -o watch watch.o $(SRC)/threads.o $(SRC)/parts.o
X
Xclock: clock.o
X	$(CC) $(CFLAGS) -o clock clock.o $(SRC)/threads.o $(SRC)/parts.o
END_OF_makefile.std
if test 248 -ne `wc -c <makefile.std`; then
    echo shar: \"makefile.std\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f parts.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"parts.c\"
else
echo shar: Extracting \"parts.c\" \(4601 characters\)
sed "s/^X//" >parts.c <<'END_OF_parts.c'
X/* parts.c -- kit of clock parts */
X
X#include "threads.h"
X#include "parts.h"
X#include <sys/types.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <string.h>
X#include <stdlib.h>
X#include <stdio.h>
X
X#define SECSperDAY (24*60*60)
X
XEXTERN char *ctime(/* time_t *t */);
X
X#define NONE 254
X
XPRIVATE int kbd_proc = NONE;	   /* Process that gets keypresses */
XPRIVATE int kbd_task = -1;	   /* UNIX process that monitors kbd */
XPRIVATE int kbd_fd;		   /* File desc. for kbd input */
X
XPRIVATE int tick_proc = NONE;	   /* Process that gets clock ticks */
XPRIVATE int tickflag = 0;	   /* Nonzero if a tick is pending */
XPRIVATE time_t now;		   /* Time of last tick */
X
Xtypedef struct {
X     bool (*d_poll)();
X     void (*d_close)();
X} device;
X
XPUBLIC int get_pid(/* void */);
XPUBLIC bool awaiting(/* int dst, int src */);
XPUBLIC void new_dev(/* device *dev */);
XPUBLIC void hw_send(/* int dst, message *msg */);
X
XFORWARD bool poll_tick();
XFORWARD bool poll_kbd();
XFORWARD void close_tick();
XFORWARD void close_kbd();
X
X/* handler -- signal handler for SIGUSER1 and SIGALRM */
XPRIVATE void handler(i)
Xint i;
X{
X     signal(i, handler);
X     if (i == SIGALRM) {
X	  alarm(1);
X	  tickflag++;
X     }	  
X}
X
XPRIVATE device tick_dev = { poll_tick, close_tick };
X
X/* conn_tick -- connect current thread to the tick */
XPUBLIC void conn_tick()
X{
X     if (tick_proc != NONE) {
X	  /* Already active */
X	  tick_proc = get_pid();
X	  return;
X     }
X
X     new_dev(&tick_dev);
X     tick_proc = get_pid();
X     signal(SIGALRM, handler);
X     alarm(1);
X}
X
X#ifndef KBD
X#define KBD "kbd"
X#endif
X
XPRIVATE device kbd_dev = { poll_kbd, close_kbd };
X
X/* conn_kbd -- connect current thread to the keyboard */
XPUBLIC void conn_kbd()
X{
X     int fd[2];
X
X     kbd_proc = get_pid();
X
X     if (kbd_task != -1)
X	  /* Already started */
X	  return;
X
X     new_dev(&kbd_dev);
X
X     pipe(fd);
X     kbd_task = fork();
X     if (kbd_task == 0) {
X	  dup2(fd[1], 1);
X	  execl(KBD, "kbd", 0);
X	  perror("kbd");
X	  exit(1);
X     }
X
X     kbd_fd = fd[0];
X     close(fd[1]);
X     signal(SIGUSR1, handler);
X     fcntl(kbd_fd, F_SETFL, O_NONBLOCK);
X}
X
X/* poll_kbd -- check for a keypress, return FALSE if nobody is waiting */
XPRIVATE bool poll_kbd()
X{
X     message m;
X     char buf[1];
X
X     if (! awaiting(kbd_proc, HARDWARE))
X	  return FALSE;
X
X     if (read(kbd_fd, buf, 1) > 0) {
X	  m.m_type = KEYPRESS;
X	  m.m_char = buf[0];
X	  hw_send(kbd_proc, &m);
X     }
X
X     return TRUE;
X}
X
X/* poll_tick -- check for a clock tick, return FALSE if nobody waiting */
XPRIVATE bool poll_tick()
X{
X     message m;
X
X     if (! awaiting(tick_proc, HARDWARE))
X	  return FALSE;
X
X     if (tickflag) {
X	  tickflag = 0;
X	  time(&now);
X	  m.m_type = TICK;
X	  hw_send(tick_proc, &m);
X     }
X
X     return TRUE;
X}
X
X/* close_kbd -- shut down the kbd process */
XPRIVATE void close_kbd()
X{
X     kill(kbd_task, SIGHUP);
X     close(kbd_fd);
X}
X
X/* close_tick -- shut down the clock tick */
XPRIVATE void close_tick()
X{
X     alarm(0);
X}
X
X/* sparks -- fill a time buffer with random junk */
XPRIVATE void sparks(s)
Xchar *s;
X{
X     static char *junk = "@!*%$&";
X     int i, k = strlen(junk);
X
X     for (i = 0; i < 8; i++)
X	  s[i] = junk[rand() % k];
X     junk[8] = '\0';
X}
X
X/* get_time -- get time as a string with offset in seconds */
XPUBLIC void get_time(offset, buf)
Xint offset;
Xchar *buf;
X{
X     time_t t;
X     int i;
X     static int entry_count = 0;
X
X     entry_count++;
X     if (entry_count > 1) {
X	  /* This is the second (or later) concurrent call */
X	  sparks(buf);
X	  return;
X     }
X
X     for (i = 0; i < 5; i++) {
X	  sched();		   /* Allow other processes to run */
X	  if (entry_count > 1) {
X	       /* One of them called get_time */
X	       sparks(buf);
X	       entry_count = 0;
X	       return;
X	  }
X     }
X
X     if (now == 0) time(&now);	   /*  avoids initial junk display */
X     t = now + offset % SECSperDAY;
X     /* ctime returns e.g. Sun Nov 17 14:18:58 1991
X	                              ^ offset 11      */
X     strncpy(buf, ctime(&t) + 11, 8);  /* Convert and extract the time */
X     buf[8] = '\0';		   /* Finish the string with a zero */
X     entry_count = 0;
X}
X
X#define NWINDOWS 4
X
X/* do_display -- process to update the display */
XPUBLIC void do_display()
X{
X     char window[NWINDOWS][10];
X     message m;
X     int i;
X
X     for (i = 0; i < NWINDOWS; i += 2) {
X	  strcpy(window[i], "----");
X	  strcpy(window[i+1], "--------");
X     }
X
X     printf("World travel clock\n\n");
X
X     while (TRUE) {
X	  printf("\r[%s]  [%s]    [%s]  [%s]  ",
X		 window[0], window[1], window[2], window[3]);
X	  fflush(stdout);
X
X	  receive(ANY, &m);
X	  strcpy(window[m.m_type], m.m_string);
X     }
X}
END_OF_parts.c
if test 4601 -ne `wc -c <parts.c`; then
    echo shar: \"parts.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f parts.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"parts.h\"
else
echo shar: Extracting \"parts.h\" \(680 characters\)
sed "s/^X//" >parts.h <<'END_OF_parts.h'
X/* parts.h -- kit of clock parts */
X
X#define SECSperHOUR 3600
X
X/* conn_tick -- connect this thread to the hardware tick */
XPUBLIC void conn_tick(/* void */);
X#define TICK -1		      /* Message type for clock tick */
X
X/* conn_kbd -- connect this thread to the keyboard */
XPUBLIC void conn_kbd(/* void */);
X#define KEYPRESS -2	      /* Message type for keyboard char */
X
X/* get_time -- get time as a string with offset in seconds */
XPUBLIC void get_time(/* int offset, char *buf */);
X
X/* do_display -- process to update display */
XPUBLIC void do_display(/* void */);
X#define UPDATE0 0	      /* Message types for the 4 windows */
X#define UPDATE1 1
X#define UPDATE2 2
X#define UPDATE3 3
END_OF_parts.h
if test 680 -ne `wc -c <parts.h`; then
    echo shar: \"parts.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f prog.sty -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"prog.sty\"
else
echo shar: Extracting \"prog.sty\" \(391 characters\)
sed "s/^X//" >prog.sty <<'END_OF_prog.sty'
X% LaTeX style for a special verbatim program environment
X
X\begingroup \catcode `|=0 \catcode `[= 1
X\catcode`]=2 \catcode `\{=12 \catcode `\}=12
X\catcode`\\=12 |gdef|@xprog#1\end{program}[#1|end[program]]
X|endgroup
X
X\def\program{\quote\begingroup
X	\@verbatim \frenchspacing\@vobeyspaces \@xprog}
X\def\endprogram{\endtrivlist\endgroup\endquote}
X
X% Make tab characters illegal
X\catcode`\^^I=15
END_OF_prog.sty
if test 391 -ne `wc -c <prog.sty`; then
    echo shar: \"prog.sty\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f threads.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"threads.c\"
else
echo shar: Extracting \"threads.c\" \(8852 characters\)
sed "s/^X//" >threads.c <<'END_OF_threads.c'
X/*
X *   threads.c -- simple lightweight processes
X */
X
X#include "threads.h"
X#include <setjmp.h>
X#include <string.h>
X#include <stdio.h>
X#include <sys/types.h>
X
X#define MAXTHREADS 128
X#define STACKSIZE 8192
X
Xtypedef struct {
X     struct thread *q_head, *q_tail;
X} queue;
X
Xtypedef struct thread {
X     char *p_name;		   /* Name for debugging */
X     void (*p_code)();		   /* Start-up code */
X     int p_status;		   /* Status (see below) */
X     int p_chan;		   /* Waiting for this pid */
X     message *p_msgbuf;		   /* Buffer for awaited message */
X     queue p_sendq;		   /* Queue of threads waiting to send */
X     struct thread *p_next;	   /* Link to next in queue */
X     jmp_buf p_cont;		   /* Continuation to resume */
X} thread;
X
X/* Values of p_status */
X#define RUNNING 1
X#define READY 2
X#define SENDING 3
X#define LISTENING 4
X#define EXITED 5
X
X/* Scheduler variables */
XPRIVATE thread ptab[MAXTHREADS];   /* Process table */
XPRIVATE int np = 0;		   /* No. of processes created */
XPRIVATE int alive;		   /* No. of processes still alive */
XPRIVATE thread *self;		   /* Current process */
XPRIVATE queue readyq;		   /* Processes ready to run */
XPRIVATE jmp_buf exit_cont;	   /* Exit continuation */
X
X/* External devices */
Xtypedef struct {
X     bool (*d_poll)();
X     void (*d_close)();
X} device;
X
X#define MAXDEVS 16
XPRIVATE int ndev = 0;
XPRIVATE device *devtab[MAXDEVS];
X
XFORWARD void startup();
XFORWARD void wakeup();
XFORWARD thread *qget();
XFORWARD void qput();
XFORWARD void qdel();
XFORWARD void panic();
XFORWARD void trace_run();
X
XEXTERN time_t time(/* time_t *t */);
XEXTERN void exit(/* status */);
X
XPUBLIC int trace_flag;
X
X#define pid(p)  ((p) - ptab)
X#define hearing(p, sender) \
X     ((p)->p_status == LISTENING && \
X      ((p)->p_chan == ANY || (p)->p_chan == sender))
X
X/* co_call -- create a thread (but don't start it yet) */
XPUBLIC int co_call(name, code)
Xchar *name;
Xvoid (*code)();
X{
X     ptab[np].p_name = name;
X     ptab[np].p_code = code;
X     return (np++);
X}
X
X/* co_start -- start up the created threads. */
XPUBLIC void co_start()
X{
X     int i;
X
X     if (np == 0) return;
X
X     if (setjmp(exit_cont) != 0) {
X	  for (i = 0; i < ndev; i++)
X	       (*devtab[i]->d_close)();
X	  return;
X     }
X
X     alive = np;
X     readyq.q_head = NULL;
X     startup(0);
X}
X
X/* startup -- start up the i'th thread.  Never returns. */
XPRIVATE void startup(i)
Xint i;
X{
X     thread *p = &ptab[i];
X
X     if (i == np) {
X	  self = qget(&readyq);
X	  self->p_status = RUNNING;
X	  trace_run(self);
X	  longjmp(self->p_cont, 1);
X     }
X
X     p->p_sendq.q_head = NULL;
X     wakeup(p);
X
X     if (setjmp(p->p_cont) != 0) {
X	  /* self == p */
X	  (*self->p_code)();
X	  self->p_status = EXITED;
X	  alive--;
X	  sched(); /* never returns */
X     }
X
X     startup1(i+1);
X}
X
X/* startup1 -- reserve stack space and call co_start */
XPRIVATE startup1(i)
Xint i;
X{
X     /* The activation record and 'space' will be overwritten by the
X        stack of the (i-1)'th thread. */
X     char space[STACKSIZE];
X     startup(i);
X}
X
X/* co_exit -- stop concurrent execution */
XPUBLIC void co_exit()
X{
X     longjmp(exit_cont, 1);
X}
X
X/* trace_mess -- report transfer of a message */
Xtrace_mess(m, dst)
Xmessage *m;
Xthread *dst;
X{
X     char *src_name = (m->m_source == HARDWARE ? "HARDWARE" :
X		       ptab[m->m_source].p_name);
X
X     if (trace_flag)
X	  printf("Message type %d from %s (pid %d) to %s (pid %d)\n",
X		 m->m_type, src_name, m->m_source, dst->p_name, pid(dst));
X}
X
X/* trace_run -- report that a process is running */
XPRIVATE void trace_run(p)
Xthread *p;
X{
X     if (trace_flag)
X	  printf("Running %s (pid %d)\n", p->p_name, pid(p));
X}
X
X/* dump_states -- show all process states */
XPRIVATE void dump_states()
X{
X     int i;
X
X     printf("\nProcess table dump:\n");
X     printf("  PID  NAME        STATUS\n");
X     for (i = 0; i < np; i++) {
X	  thread *p = &ptab[i];
X
X	  printf("  %3d  %-10s  ", i, p->p_name);
X	  switch (p->p_status) {
X	  case RUNNING:
X	       printf("running\n");
X	       break;
X	  case READY:
X	       printf("ready\n");
X	       break;
X	  case SENDING:
X	  case LISTENING:
X	       printf("waiting to %s %s (pid %d)\n",
X		      (p->p_status == SENDING ? "send to" : "receive from"),
X		      (p->p_chan == HARDWARE ? "HARDWARE"
X		       : p->p_chan == ANY ? "ANY"
X		       : ptab[p->p_chan].p_name),
X		      p->p_chan);
X	       break;
X	  case EXITED:
X	       printf("exited\n");
X	  }
X     }
X     printf("\n");
X}
X
X/* sched -- find a ready process and transfer control to it */
XPUBLIC void sched()
X{
X     if (self->p_status == RUNNING)
X	  /* Current process still ready -- queue it again */
X	  wakeup(self);
X
X     /* Look for something to do */
X     while (TRUE) {
X	  int pausing = FALSE;
X	  int i;
X
X	  for (i = 0; i < ndev; i++)
X	       if ((*devtab[i]->d_poll)())
X		    pausing = TRUE;
X
X	  if (readyq.q_head != NULL) {
X	       if (setjmp(self->p_cont) != 0)
X		    return;
X	       self = qget(&readyq);
X	       self->p_status = RUNNING;
X	       trace_run(self);
X	       longjmp(self->p_cont, 1);
X	  }
X     
X	  /* Nothing to do now.  Pause if anything might happen later. */
X	  if (pausing)
X	       pause();
X	  else if (alive != 0) {
X	       dump_states();
X	       panic("deadlock!");
X	  }
X	  else
X	       longjmp(exit_cont, 1);
X     }
X}
X
X/* send -- send a message. */
XPUBLIC void send(dst, m)
Xint dst;
Xmessage *m;
X{
X     thread *rcvr = &ptab[dst];
X
X     if (dst < 0 || dst >= np) 
X	  panic("sending to non-existent process");
X
X     m->m_source = pid(self);
X
X     if (hearing(rcvr, pid(self))) {
X	  /* receiver is waiting to hear from us -- wake him up */
X	  trace_mess(m, rcvr);
X	  *rcvr->p_msgbuf = *m;
X	  wakeup(rcvr);
X     } else {
X	  /* receiver is otherwise engaged -- wait */
X	  self->p_status = SENDING;
X	  self->p_chan = dst;
X	  self->p_msgbuf = m;
X	  qput(self, &rcvr->p_sendq);
X	  sched();
X     }
X}
X
X/* hw_send -- send a message from hardware. */
XPUBLIC void hw_send(dst, m)
Xint dst;
Xmessage *m;
X{
X     thread *rcvr = &ptab[dst];
X
X     if (dst < 0 || dst >= np) 
X	  panic("sending to non-existent process");
X
X     m->m_source = HARDWARE;
X     if (hearing(rcvr, HARDWARE)) {
X	  /* receiver is waiting to hear from us -- wake him up */
X	  trace_mess(m, rcvr);
X	  *rcvr->p_msgbuf = *m;
X	  wakeup(rcvr);
X     }
X}
X
X/* receive -- receive a message or wait for one. */
XPUBLIC void receive(src, m)
Xint src;
Xmessage *m;
X{
X     thread *sndr, *prev = NULL;
X
X     if (src != ANY && src != HARDWARE && (src < 0 || src > np))
X	  panic("receiving from non-existent process");
X
X     /* Look for a waiting sender */
X     for (sndr = self->p_sendq.q_head; sndr != NULL;
X	       prev = sndr, sndr = sndr->p_next) {
X	  if (src == ANY || src == pid(sndr)) {
X	       qdel(sndr, prev, &self->p_sendq);
X	       break;
X	  }
X     }
X
X     if (sndr != NULL) {
X	  /* Someone is waiting to send */
X	  *m = *sndr->p_msgbuf;
X	  trace_mess(m, self);
X	  wakeup(sndr);
X     } else {
X	  /* Nobody is waiting -- go to sleep */
X	  self->p_status = LISTENING;
X	  self->p_chan = src;
X	  self->p_msgbuf = m;
X	  sched();
X     }
X}
X
X/* get_pid -- get current process id */
XPUBLIC int get_pid()
X{
X     return (pid(self));
X}
X
X/* awaiting -- test if dst is waiting to hear from src */
XPUBLIC bool awaiting(dst, src)
Xint dst, src;
X{
X     return (hearing(&ptab[dst], src));
X}
X
X/* new_dev -- register an asynchronous device */
XPUBLIC void new_dev(dev)
Xdevice *dev;
X{
X     devtab[ndev++] = dev;
X}
X
X/* wakeup -- mark a process as ready */
XPRIVATE void wakeup(p)
Xthread *p;
X{
X     p->p_status = READY;
X     qput(p, &readyq);
X}
X
X/* qget -- take an item from the head of a queue */
XPRIVATE thread *qget(q)
Xqueue *q;
X{
X     thread *p;
X
X     if (q->q_head == NULL)
X	  panic("removing item from empty queue");
X
X     p = q->q_head;
X     q->q_head = p->p_next;
X     return p;
X}
X
X/* qput -- add an item to the tail of a queue */
XPRIVATE void qput(p, q)
Xthread *p;
Xqueue *q;
X{
X     if (q->q_head == NULL)
X	  q->q_head = p;
X     else
X	  q->q_tail->p_next = p;
X     p->p_next = NULL;
X     q->q_tail = p;
X}
X
X/* qdel -- delete an item from the middle of a queue */
XPRIVATE void qdel(item, prev, q)
Xthread *item, *prev;
Xqueue *q;
X{
X     if (item != q->q_head && item != prev->p_next)
X	  panic("bad args to qdel");
X
X     if (item == q->q_head)
X	  q->q_head = item->p_next;
X     else {
X	  prev->p_next = item->p_next;
X	  if (q->q_tail == item) q->q_tail = prev;
X     }
X}
X
XPRIVATE void panic(msg)
Xchar *msg;
X{
X     fprintf(stderr, "Panic: %s\n", msg);
X     longjmp(exit_cont, 1);
X}
X
XPUBLIC void send_int(dst, type, val)
Xint dst, type;
Xint val;
X{
X     message m;
X     m.m_type = type;
X     m.m_int = val;
X     send(dst, &m);
X}
X
XPUBLIC void send_char(dst, type, val)
Xint dst, type;
Xchar val;
X{
X     message m;
X     m.m_type = type;
X     m.m_char = val;
X     send(dst, &m);
X}
X
XPUBLIC void send_string(dst, type, val)
Xint dst, type;
Xchar *val;
X{
X     message m;
X     m.m_type = type;
X     strcpy(m.m_string, val);
X     send(dst, &m);
X}
END_OF_threads.c
if test 8852 -ne `wc -c <threads.c`; then
    echo shar: \"threads.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f threads.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"threads.h\"
else
echo shar: Extracting \"threads.h\" \(1902 characters\)
sed "s/^X//" >threads.h <<'END_OF_threads.h'
X/* threads.h */
X
X/* MINIX-style keywords for declarations */
X#define PRIVATE static
X#define PUBLIC
X#define EXTERN extern
X#define FORWARD static
X
X/* Booleans */
Xtypedef int bool;
X#define TRUE 1
X#define FALSE 0
X
X/* Message format */
Xtypedef struct {
X     int m_source;                 /* PID of the sender */
X     int m_type;                   /* Type of message   */
X     union {                       /* Contents:         */
X          int m__int;              /*   a number,       */
X          char m__char;            /*   a character,    */
X          char m__string[10];      /*   or a string.    */
X     } m_u;
X} message;
X
X/* Short form selectors */
X#define m_int m_u.m__int
X#define m_char m_u.m__char
X#define m_string m_u.m__string
X
X/* These definitions prevent a name clash
X   with the MINIX primitives: */
X#define send _send_
X#define receive _receive_
X
X/* Interface functions */
X
X/* co_call -- create a process to run later */
XPUBLIC int  co_call(/* char *name, void (*code)() */);
X
X/* co_start -- start up the processes created earlier */
XPUBLIC void co_start(/* void */);
X
X/* sched -- allow other processes to run */
XPUBLIC void sched(/* void */);
X
X/* send -- send a message and block until it is received */
XPUBLIC void send(/* int dst, message *msg */);
X
X/* receive -- receive a message and block until one arrives */
XPUBLIC void receive(/* int src, message *msg */);
X
X/* co_exit -- terminate all threads immediately */
XPUBLIC void co_exit(/* void */);
X
X/* send_??? -- convenient forms of send */
XPUBLIC void send_int(/* int dst, int type, int val */);
XPUBLIC void send_char(/* int dst, int type, char val */);
XPUBLIC void send_string(/* int dst, int type, string val */);
X
X/* Special processes */
X#define HARDWARE -1		   /* Where ticks and keypresses come from */
X#define ANY 255			   /* Use as src to accept any message */
X
XEXTERN int trace_flag;		   /* Set non-zero for message tracing */
END_OF_threads.h
if test 1902 -ne `wc -c <threads.h`; then
    echo shar: \"threads.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f watch.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"watch.c\"
else
echo shar: Extracting \"watch.c\" \(748 characters\)
sed "s/^X//" >watch.c <<'END_OF_watch.c'
X/* watch -- a very simple clock */
X
X#include "threads.h"
X#include "parts.h"
X
X/* Processes */
XPRIVATE int keyboard, update, display;
X
XPRIVATE void do_keyboard()
X{
X     message m;
X
X     conn_kbd();
X
X     for (;;) {
X	  receive(HARDWARE, &m);
X	  if (m.m_char == '.') co_exit();
X     }
X}
X
XPRIVATE void do_update()
X{
X     message m;
X     char buf[10];
X
X     conn_tick();
X     send_string(display, UPDATE0, "TIME");
X
X     for (;;) {
X	  get_time(0, buf);
X	  send_string(display, UPDATE1, buf);
X	  receive(HARDWARE, &m);
X     }
X}
X
Xmain()
X{
X     keyboard = co_call("keyboard", do_keyboard);
X     update   = co_call("update",   do_update);
X     display  = co_call("display",  do_display);
X     co_start();
X     printf("\n\n");		   /* tidy up the display */
X}
END_OF_watch.c
if test 748 -ne `wc -c <watch.c`; then
    echo shar: \"watch.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0
