+++ /dev/null
-/*
- * server.c
- *
- * lwp (echo) connection handler server
- *
- */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-
-#define SP_ACCEPT 3
-#define SP_READER 3
-
-#include "lwp.h"
-
-struct context {
- struct lwpProc *us;
- struct sockaddr_in addr;
- int fd;
-};
-
-/*ARGSUSED*/
-int
-readConn(argc, argv, ud)
-int argc;
-char **argv;
-void *ud;
-{
- struct context *ctx = (struct context *)ud;
- char buf[1024];
- int n;
-
- while (1) {
- printf("sleeping\n");
- lwpSleepFd(ctx->fd, LWP_FD_READ);
- printf("waiting to read\n");
- if ((n = read(ctx->fd, buf, sizeof(buf))) <= 0)
- break;
- printf("got %d char\n", n);
- lwpSleepFd(ctx->fd, LWP_FD_WRITE);
- printf("waiting to write\n");
- if (write(ctx->fd, buf, n) < 0)
- break;
- printf("wrote %d char\n", n);
- }
- printf("process/fd %d exiting\n", ctx->fd);
- close(ctx->fd);
- lwpExit();
- /*NOTREACHED*/
-}
-
-int
-acceptConn(argc, argv)
-int argc;
-char **argv;
-{
- struct sockaddr_in sin;
- int s;
- int ns;
- int len;
- int maxfd;
- struct context *ctx;
-
- if (argc != 2) {
- fprintf(stderr, "Usage: %s port\n", *argv);
- exit(-1);
- }
- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- perror("inet socket");
- exit(-1);
- }
- sin.sin_family = AF_INET;
- sin.sin_port = htons(atoi(argv[1]));
- sin.sin_addr.s_addr = 0;
- if (bind(s, &sin, sizeof(sin)) < 0) {
- perror("inet socket bind");
- exit(-1);
- }
- if (listen(s, LISTENMAXCONN) < 0) {
- perror("inet socket listen");
- exit(-1);
- }
- maxfd = getdtablesize() - 1;
- while (1) {
- lwpSleepFd(s, LWP_FD_READ);
- len = sizeof(sin);
- ns = accept(s, &sin, &len);
- if (ns < 0) {
- perror("accept");
- exit(-1);
- }
- if (ns == maxfd) {
- fprintf(stderr, "no more connections");
- close(ns);
- }
- printf("got connection from %s\n", inet_ntoa(sin.sin_addr));
- ctx = (struct context *)malloc(sizeof(*ctx));
- ctx->addr = sin;
- ctx->fd = ns;
- ctx->us = lwpCreate(SP_READER, readConn, 8192, 0, 0, ctx);
- }
- /*NOTREACHED*/
-}
-
-int
-main(argc, argv)
-int argc;
-char **argv;
-{
- lwpInitSystem(1);
- lwpCreate(SP_ACCEPT, acceptConn, 8192, argc, argv, 0);
- lwpReschedule();
- /*NOTREACHED*/
-}
+++ /dev/null
-/*
- * lwp.h -- prototypes and structures for lightweight processes
- * Copyright (C) 1991-3 Stephen Crane.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * author: Stephen Crane, (jsc@doc.ic.ac.uk), Department of Computing,
- * Imperial College of Science, Technology and Medicine, 180 Queen's
- * Gate, London SW7 2BZ, England.
- */
-#ifndef _LWP_H
-#define _LWP_H
-
-#include <setjmp.h>
-#include <sys/time.h>
-
-/* process control block. do *not* change the position of context */
-struct lwpProc {
- jmp_buf context; /* processor context area */
- void *sbtm; /* bottom of stack attached to it */
- int size; /* size of stack */
- void (*entry) (); /* entry point */
- int dead; /* whether the process can be rescheduled */
- int pri; /* which scheduling queue we're on */
- long runtime; /* time at which process is restarted */
- int fd; /* fd we're blocking on */
- int argc; /* initial arguments */
- char **argv;
- void *ud; /* user data */
- struct lwpProc *next;
-};
-
-/* queue */
-struct lwpQueue {
- struct lwpProc *head;
- struct lwpProc *tail;
-};
-
-/* semaphore */
-struct lwpSem {
- int count;
- struct lwpQueue q;
-};
-
-#define LWP_FD_READ 0x1
-#define LWP_FD_WRITE 0x2
-
-#define LWP_MAX_PRIO 8
-
-struct lwpProc *lwpInitSystem(int);
-struct lwpProc *lwpCreate(int, void (*)(void *), int, int, char **, void *);
-void lwpExit(void);
-void lwpTerminate(struct lwpProc *);
-void lwpYield(void);
-void lwpSleepFd(int fd, int flags);
-void lwpSleepUntil(long until);
-void lwpWakeupFd(struct lwpProc *);
-void *lwpGetUD(struct lwpProc *);
-void lwpSetUD(struct lwpProc *, char *);
-int lwpSetPriority(int);
-void lwpReschedule();
-
-struct lwpSem *lwpCreateSem(int);
-void lwpSignal(struct lwpSem *);
-void lwpWait(struct lwpSem *);
-void lwpSelect(int argc, char **argv);
-
-extern struct lwpProc *LwpCurrent;
-
-#endif /* _LWP_H */
+++ /dev/null
-\documentstyle[tgrind, a4]{article}
-\title{The {\sc Rex} lightweight process library}
-\author{Stephen Crane\\ (jsc@doc.ic.ac.uk)\thanks{Thanks to Mark Little
-(m.c.little@ncl.ac.uk) for the Linux port}}
-\begin{document}
-\maketitle
-This document describes the interface to and the behaviour underlying
-my threads library for Rex.\footnote{Available as lwp.tar.gz by anonymous
-ftp from gummo.doc.ic.ac.uk:/rex. Rex (Esprit project 2080) was
-axed by Men in Suits.} It has been tested on Sun-3, Sun-4,
-Mips, 386-BSD and Linux systems. Counting semi-colons, it is
-260 lines long, including support for the different architectures (this
-figure includes variable- but not function-declarations).
-
-A word from our sponsor:
-\begin{quote}
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public
-License as published by the Free Software Foundation; either
-version 2 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but {\sc WITHOUT ANY WARRANTY}; without even the implied warranty of
-{\sc MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE}. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; if not, write to the Free
-Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-\end{quote}
-
-(Note that while this library is protected by the GNU copyleft, it is not
-supported by the Free Software Foundation.)
-
-\section{Threads}
-Threads are prioritised and
-non-preemptive. Operations supported on threads are:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{struct} pcb *initlp (\K{int} priority)}}
-\L{\LB{}\Tab{8}{\K{struct} pcb *creatp (priority, entry, size, argc, argv, envp)}}
-\L{\LB{}\Tab{8}{\K{void} readyp (\K{struct} pcb *p)}}
-\L{\LB{}\Tab{8}{\K{void} yieldp (\K{void})}}
-\L{\LB{}\Tab{8}{\K{void} *getenvp (\K{struct} pcb *p)}}
-\L{\LB{}\Tab{8}{\K{void} setenvp (\K{struct} pcb *p, \K{void} *)}}
-\L{\LB{}\Tab{8}{\K{void} suicidep (\K{void})}}
-\L{\LB{}\Tab{8}{\K{void} destroyp (\K{struct} pcb *p)}}
-\end{tgrind}
-\begin{description}
-\item[initlp] initialises the threads runtime, creating a thread with
-specified priority for the invoker.
-\item[creatp] creates a new thread with specified {\em priority}, {\em
-entry} point, with a stack of {\em size}, {\em argc} arguments in {\em
-argv} and a user-defined environment pointer.
-\item[getenvp] returns the environment pointer associated with the given
-thread. If the thread is null, the current thread is assumed.
-\item[setenvp] reassigns the environment pointer associated with the
-given thread.
-\item[readyp] makes the specified thread ready to run, or the current
-thread if null.
-\item[yieldp] makes the current thread ready to run. If no thread of
-higher priority is runnable, the current thread will run.
-\item[suicidep] marks the invoking thread as dead. It will never be
-rescheduled.
-\item[destroyp] marks the specified thread as dead. It will be removed
-at the next reschedule. If it is currently running, it will be
-unaffected until the next reschedule.
-\end{description}
-
-\section{Semaphores}
-For synchronisation, counting semaphores are provided. Available
-operations are:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{struct} sem *creats (\K{int} count)}}
-\L{\LB{}\Tab{8}{\K{void} signals (\K{struct} sem *s)}}
-\L{\LB{}\Tab{8}{\K{void} waits (\K{struct} sem *s)}}
-\end{tgrind}
-\begin{description}
-\item[creats] allocates a new semaphore from the heap and initialises its
-count.
-\item[signals] increments the semaphore's count, makes a waiting process
-ready if there is one. If the readied process's priority is greater than
-that of the signaller, a reschedule is done.
-\item[waits] decrements the semaphore's count. If it becomes negative,
-the current process is suspended and a reschedule is done.
-\end{description}
-
-\section{Signals}
-The library is concerned with two types of signal, {\sc sigio} and {\sc
-sigalrm}. These signals are normally blocked until the null process is
-scheduled. It uses {\tt sigpause()} to reenable them and wait for one
-to arrive. While awaiting a signal, the null process `runs' at maximum
-priority. Thus users will not be able to cause a reschedule from
-their handlers. When {\tt sigpause()} returns, the signal will have
-been handled and the null process drops back down to the lowest priority
-and yields to any thread which has been made ready to run from the
-user-level handler.
-
-These semantics make the library rather unresponsive to signals in the
-presence of busy processes. If a more responsive system is required,
-the constant {\sc LCOUNT} may be changed. This value determines the
-number of times the {\tt reschedp ()} function must be called before
-signals are re-enabled. If given a value of {\tt 1}, it will affect
-context-switching time by about 50\%. Its default value is {\tt -1}.
-
-\subsection{Input and output}
-Input and output present a problem to threads, because they require
-calls to the underlying {\sc Unix} system, which will block not only
-the invoking thread, but also all others in the process. Thus, in
-general, a thread must wait until the descriptor on which I/O is to
-be done becomes ready for the operation. This is done by trapping
-{\sc sigio}. Two routines are provided:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{int} sigioset (\K{int} fd, \K{void} (*han) (\K{void}
-*, \K{int}), \K{void} *ctx)}}
-\L{\LB{}\Tab{8}{\K{int} sigioclr (\K{int} fd)}}
-\end{tgrind}
-The general model adopted for processing {\sc sigio} is to install a
-{\em handler} routine for the I/O descriptor using {\em sigioset} and
-remove it using {\em sigioclr}. The user is responsible for setting up
-the device correctly to generate {\sc sigio}. When {\sc sigio} arrives
-for the descriptor, the handler will be called with a context pointer
-as its argument. (In C++, this context is the instance pointer of the
-invoking thread.)
-
-\subsection{The timer}
-A single routine is provided to block the invoking thread for the
-specified time:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{void} delayp (\K{int} n)}}
-\end{tgrind}
-This routine blocks the invoker for {\em at least\/} the time specified.
-If this is zero, a reschedule is done. Delays are implemented as a
-delta queue, using {\sc sigalrm}. $n$ specifies a microsecond delay
-which is of limited utility in practice.
-
-\section{Performance}
-\begin{figure}[htb]
-\begin{center}
-\begin{tabular}{||l|c|c|c||} \hline
-Arch & ctxsw & creat & comment \\ \hline
-sun3 & 308 & 778 & 3/240 \\ \hline
-386bsd & 186 & 464 & 486/33 \\ \hline
-sun4 & 96 & 436 & IPX \\ \hline
-sun4 & 59 & 212 & Sparc-10 \\ \hline
-linux & 56 & 382 & 486-DX2/50 \\ \hline
-mips & 17 & 85 & Decstation \\ \hline
-\end{tabular}
-\caption{Performance with architecture (times in microseconds).}
-\end{center}
-\end{figure}
-\begin{description}
-\item[sun3] has very lightweight process initialisation, compared with
-context switching.
-\item[sun4] has a high context switch time as a result of {\tt setjmp ()} and
-{\tt longjmp ()} implementations. Process initialisation is also relatively
-heavyweight: it requires two calls to {\tt setjmp ()} and one to {\tt longjmp
-()}.
-\item[mips] provides its own context switching in assembly language.
-\end{description}
-
-\section{Porting to another architecture}
-Although the threads library is quite portable, a few guidelines should
-be observed when moving to a new architecture.
-
-\begin{itemize}
-\item Create two new files for your architecture/kernel (e.g. {\tt sun4.c}
-and {\tt sun4.h}).
-The `.c' file should contain any routines which your version of {\sc Unix} is
-missing and a thread initialisation routine:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{void} initp (\K{struct} pcb *, \K{void} *)}}
-\end{tgrind}
-The `.h' file contains any machine-specific definitions.
-\item If {\tt setjmp ()} and {\tt longjmp ()} don't work on your machine
-(for example the {\sc Ultrix} {\tt longjmp ()} implementation checks that
-the frame being jumped to is an ancestor of the current one), you will
-have to write {\tt savep ()} and {\tt restorep ()} and put the following
-define into your machine-specific header file:
-\begin{tgrind}
-\L{\LB{}\Tab{8}{\K{\#define} OWN\_CONTEXT\_SWITCH}}
-\end{tgrind}
-\item Compile and run the three test programs: {\tt producer.c}, {\tt
-timer.c} and {\tt bm.c}.
-\item Add the name of the architecture to `config'.
-\item Send {\tt new-arch.[ch]}, any context diffs for the rest of the
-library and the output of {\tt bm.c} to {\tt jsc@doc.ic.ac.uk}. Also
-please let me know where you got the library from.
-\end{itemize}
-\end{document}