Initial revision

This commit is contained in:
ceriel 1987-02-24 18:36:02 +00:00
parent bc94559e4d
commit 2eeacf071a
17 changed files with 1034 additions and 0 deletions

11
lang/occam/lib/AR2 Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
case $# in
0) ar ru /user0/bot/lib/lib2.a *.o
ranlib /user0/bot/lib/lib2.a
rm -f *.o
;;
*) keys=$1
shift
ar $keys /user0/bot/lib/lib2.a $*
;;
esac

11
lang/occam/lib/AR4 Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
case $# in
0) ar ru /user0/bot/lib/lib4.a *.o
ranlib /user0/bot/lib/lib4.a
rm -f *.o
;;
*) keys=$1
shift
ar $keys /user0/bot/lib/lib4.a $*
;;
esac

28
lang/occam/lib/Makefile Normal file
View file

@ -0,0 +1,28 @@
PRIMITIVES= par_vax.s
PARALLEL= parco.c par.c co.c
OCRT= ocrt.c builtin.c channel.c chan_struct.c
COMMON= $(PRIMITIVES) $(PARALLEL) $(OCRT)
SIZE2= em2.e
SIZE4= em4.e
LIB2= /user0/bot/lib/lib2.a
LIB4= /user0/bot/lib/lib4.a
ACK2= vax2
ACK4= vax4
all: lib2 lib4
lib2: $(COMMON) $(SIZE2)
rm -f *.o $(LIB2)
$(ACK2) -c.o -L -Dvoid=char -Dptrdiff=long $(COMMON) $(SIZE2)
ar cq $(LIB2) *.o
rm -f *.o
lib4: $(COMMON) $(SIZE4)
rm -f *.o $(LIB4)
$(ACK4) -c.o -L -Dvoid=char $(COMMON) $(SIZE4)
ar cq $(LIB4) *.o
rm -f *.o

75
lang/occam/lib/builtin.c Normal file
View file

@ -0,0 +1,75 @@
/* builtin.c - built in named processes */
#include "channel.h"
#ifndef nil
#define nil 0
#endif
extern int errno;
static void nullterm(s) register char *s;
/* Change Occam string to C string */
{
register len= (*s & 0377);
register char *p;
while (--len>=0) {
p=s++;
*p = *s;
}
*s=0;
}
static void lenterm(s) register char *s;
/* Change C string to Occam string */
{
register i=0;
register c0, c1;
c0=0;
do {
c1=s[i];
s[i++]=c0;
c0=c1;
} while (c0!=0);
*s= i-1;
}
void b_open(mode, name, index) register char *mode, *name; long *index;
/* PROC open(VAR index, VALUE name[], mode[])= */
{
register FILE *fp;
register i;
nullterm(name);
nullterm(mode);
fp=fopen(name, mode);
lenterm(name);
lenterm(mode);
if (fp==nil)
*index= -errno;
else {
/* Find free file channel, there must be one free! */
for (i=0; (file[i].f.flgs&C_F_INUSE)!=0; i++) ;
file[i].f.flgs|=C_F_INUSE;
unix_file[i]=fp;
*index=i;
}
}
void b_close(index) long index;
/* PROC close(VALUE index)= */
{
fclose(unix_file[index]);
file[index].f.flgs&= ~C_F_INUSE;
}
void b_exit(code) long code;
/* PROC exit(VALUE code)= */
{
exit((int) code);
}

152
lang/occam/lib/channel.c Normal file
View file

@ -0,0 +1,152 @@
/* channel.c - basic channel handling routines */
#include <errno.h>
#include <signal.h>
#include <sgtty.h>
#include "channel.h"
static void disaster();
void c_init(c, z) register chan *c; register unsigned z;
/* Initialise an array of interprocess channels declared as: CHAN c[z]. */
{
do {
c->type=C_T_CHAN;
(c++)->c.synch=C_S_FREE;
} while (--z!=0);
}
void chan_in(v, c) long *v; register chan *c;
/* Reads a value from channel c and returns it through v. */
{
switch(c->type) {
case C_T_FILE:
if ((c->f.flgs&C_F_READAHEAD)!=0) {
*v=(c->f.preread&0377);
c->f.flgs&= ~C_F_READAHEAD;
} else {
register FILE *fp= unix_file[c->f.index];
*v= feof(fp) ? C_F_EOF : getc(fp);
}
break;
case C_T_CHAN:
deadlock=0; /* Wait for value to arrive */
while (c->c.synch!=C_S_ANY) resumenext();
*v=c->c.val;
c->c.synch=C_S_ACK; /* Acknowledge receipt */
break;
default:
disaster();
}
}
void chan_out(v, c) long v; register chan *c;
/* Send value v through channel c. */
{
switch(c->type) {
case C_T_FILE: {
register FILE *fp= unix_file[c->f.index];
struct sgttyb tty;
if ((v& ~0xff)==0) /* Plain character */
putc( (int) v, fp);
else
if (v==C_F_TEXT) {
ioctl(fileno(fp), TIOCGETP, &tty);
tty.sg_flags&= ~CBREAK;
tty.sg_flags|= ECHO|CRMOD;
ioctl(fileno(fp), TIOCSETN, &tty);
} else
if (v==C_F_RAW) {
ioctl(fileno(fp), TIOCGETP, &tty);
tty.sg_flags|= CBREAK;
tty.sg_flags&= ~(ECHO|CRMOD);
ioctl(fileno(fp), TIOCSETN, &tty);
}
} break;
case C_T_CHAN:
deadlock=0; /* Wait until channel is free */
while (c->c.synch!=C_S_FREE) resumenext();
c->c.val=v;
c->c.synch=C_S_ANY; /* Channel has data */
deadlock=0; /* Wait for acknowledgement */
while (c->c.synch!=C_S_ACK) resumenext();
c->c.synch=C_S_FREE; /* Back to normal */
break;
default:
disaster();
}
}
static int timeout();
int chan_any(c) register chan *c;
{
switch (c->type) {
case C_T_FILE:
if ((c->f.flgs&C_F_READAHEAD)!=0)
return 1;
else {
register FILE *fp= unix_file[c->f.index];
if (feof(fp))
return 1;
else {
extern int errno;
register ch;
deadlock=0;
/* No deadlock while waiting for key */
signal(SIGALRM, timeout);
alarm(1);
errno=0;
ch=getc(fp);
signal(SIGALRM, SIG_IGN);
alarm(0);
if (errno==EINTR)
return 0;
else {
if (!feof(fp)) {
c->f.flgs|=C_F_READAHEAD;
c->f.preread=ch;
}
return 1;
}
}
}
case C_T_CHAN:
return c->c.synch==C_S_ANY;
default:
disaster();
}
}
/* The ch=getc(fp) in the above function calls read(2) to do its task, but if
* there's no input on the file (pipe or terminal) then the read will block.
* To stop this read from blocking, we use the fact that if the read is
* interrupted by a signal that is caught by the program, then the read returns
* error EINTR after the signal is processed. Thus we use a one second alarm
* to interrupt the read with a trap to timeout(). But since the alarm signal
* may occur *before* the read is called, it is continuously restarted in
* timeout() to prevent it from getting lost.
*/
static int timeout(sig)
{
signal(SIGALRM, timeout);
alarm(1);
}
static void disaster()
{
write(2, "Fatal error: Channel variable corrupted\n", 40);
abort();
}

115
lang/occam/lib/co.c Normal file
View file

@ -0,0 +1,115 @@
/* co.c - Routines to handle coroutines */
#include "process.h"
static void search(), RESUMERR();
void resume(id) identification id;
/* Stops the current process, by saving its stack, and searches for the
* process with identification 'id' in the group the running process
* belongs to. If 'id' cannot be found then repeat these actions with
* the running process' parent. If 'id' is found it is activated. It
* is a fatal error if 'id' cannot be found.
*/
{
if (group!=nil) {
register wordsize size;
size=top_size(group->s_brk);
(*group->active)->stack=alloc((unsigned) size);
if (top_save(size, (*group->active)->stack))
search(id);
else {
free((*group->active)->stack);
load_betweens();
}
} else
RESUMERR();
}
static void search(id) identification id;
/* Searches for the process with identification 'id'.
* If the process is found it is activated and its process tree is
* traversed to find the running process.
*/
{
register struct process **aproc, *proc;
for(;;) {
aproc= &group->first;
while (*aproc!=nil && (*aproc)->id!=id)
aproc= &(*aproc)->next;
if (*aproc!=nil) break;
save_between(group);
if ((group=group->up)==nil)
RESUMERR();
}
group->active=aproc;
proc= *aproc;
highest_group=group;
while (proc->down!=nil) {
group=proc->down;
proc= *group->active;
}
top_load(proc->stack);
}
static void delete_group(group) struct procgroup *group;
/* Removes the whole group and sub-groups recursively from the running
* process.
*/
{
register struct process *proc, *next;
proc=group->first;
while (proc!=nil) {
if (proc->down!=nil)
delete_group(proc->down);
else
free(proc->stack);
next=proc->next;
free( (void *) proc);
proc=next;
}
delete_between(group);
free( (void *) group);
}
void coend()
{
register struct process *proc, *next;
register struct procgroup *junk;
proc=group->first;
while (proc!=nil) {
if (proc!= *group->active) {
if (proc->down!=nil)
delete_group(proc->down);
else
free(proc->stack);
}
next=proc->next;
free( (void *) proc);
proc=next;
}
delete_between(group);
junk=group;
group=group->up;
free( (void *) junk);
if (group!=nil)
(*group->active)->down=nil;
}
static void RESUMERR()
{
write(2, "RESUMERR\n", 9);
abort();
}

52
lang/occam/lib/em2.e Normal file
View file

@ -0,0 +1,52 @@
mes 2,2,4
oldtrp
bss 4, 0, 0
exp $init
pro $init, 0
loc -321-1
sim
lpi $catch1
sig
sde oldtrp
cal $initfile
ret 0
end 0
pro $catch1, 0
lde oldtrp
sig
asp 4
loe 0
lde 4
lol 0
cal $catch
asp 8
lol 0
trp
rtt
end 0
exp $now
pro $now, 12
zre deadlock
lal -12
loc 35
mon
asp 2
ldl -12
ret 4
end 12
exp $block_mo
pro $block_mo, 0
ldl 4
ldl 8
ldl 0
loc 4
loc 2
cuu
bls 2
ret 0
end 0

49
lang/occam/lib/em4.e Normal file
View file

@ -0,0 +1,49 @@
mes 2,4,4
oldtrp
bss 4, 0, 0
exp $init
pro $init, 0
loc -321-1
sim
lpi $catch1
sig
ste oldtrp
cal $initfile
ret 0
end 0
pro $catch1, 0
loe oldtrp
sig
asp 4
loe 0
loe 4
lol 0
cal $catch
asp 12
lol 0
trp
rtt
end 0
exp $now
pro $now, 12
zre deadlock
lal -12
loc 35
mon
asp 4
lol -12
ret 4
end 12
exp $block_mo
pro $block_mo, 0
lol 4
lol 8
lol 0
bls 4
ret 0
end 0

47
lang/occam/lib/ocm_chan.h Normal file
View file

@ -0,0 +1,47 @@
/* channel.h - channel definitions */
#include <stdio.h>
#include "parco.h"
typedef union channel {
struct { /* Interprocess channel */
char _type; /* Channel type, see note */
char synch; /* State in channel synchronization */
long val; /* Transmitted value */
} c;
struct { /* File channel */
char _type; /* Dummy field, see note */
char index; /* Index in the file array */
char flgs; /* Status flags: in use & readahead */
char preread; /* Possible preread character */
} f;
} chan;
#define type c._type /* Channel type */
/* Note: The channel type should not be part of each structure in chan. But
* the C alignment rules would make chan about 50% bigger if we had done it
* the right way. Note that the order of fields in a struct cannot be a problem
* as long as struct c is the largest within the union.
*/
#define C_T_CHAN 0 /* Type of a interprocess channel */
#define C_T_FILE 1 /* Type of a file channel */
#define C_S_FREE 0 /* IP channel is free */
#define C_S_ANY 1 /* IP channel contains data */
#define C_S_ACK 2 /* IP channel data is removed */
#define C_F_EOF (-1L) /* File channel returns EOF */
#define C_F_TEXT (-2L) /* File channel becomes line oriented */
#define C_F_RAW (-3L) /* File channel becomes character oriented */
#define C_F_INUSE 0x01 /* File channel is connected to a UNIX file */
#define C_F_READAHEAD 0x02 /* File channel has a preread character */
extern chan file[_NFILE]; /* Array of file channels */
extern FILE *unix_file[_NFILE]; /* Pointers to buffered UNIX files */
void c_init();
void chan_in(), cbyte_in(), c_wa_in(), c_ba_in();
void chan_out(), c_wa_out(), c_ba_out();
int chan_any();

View file

@ -0,0 +1,18 @@
/* parco.h - Define names for simulation routines
*
* This file is to be included by users of the higher-level routines
*
*/
void pc_begin(), resumenext(), parend(), resume(), coend();
int pc_fork();
#define nullid ((int *) 0 - (int *) 0)
/* I.e. a 0 of type "pointer difference" */
#define parbegin(sbrk) pc_begin(sbrk, nullid)
#define parfork() pc_fork(nullid)
#define cobegin(sbrk, id) pc_begin(sbrk, id)
#define cofork(id) pc_fork(id)
extern int deadlock;

52
lang/occam/lib/ocm_proc.h Normal file
View file

@ -0,0 +1,52 @@
/* process.h - Define administration types and functions
*
* This file is to be included by implementors of the higher
* level routines
*
*/
#include "parco.h"
#ifndef ptrdiff /* This type must be able to hold a pointer difference */
#define ptrdiff int /* Define as long int if necessary */
#endif
#define nil 0
void *alloc(), free();
typedef ptrdiff wordsize, identification;
wordsize top_size();
int top_save();
void top_load(); /* Primitives */
struct procgroup;
struct process {
struct process *next; /* Next process in the same group */
struct procgroup *down; /* Process group running under this process */
void *stack; /* Pointer to the saved stack top */
identification id; /* Coroutine identification */
};
#define init_between __i_b__ /* These names are hidden */
#define save_between __s_b__
#define load_betweens __l_b__
#define delete_between __d_b__
void init_between(), save_between(), load_betweens(), delete_between();
struct procgroup {
struct process **active;/* Active process within this group */
struct procgroup *up; /* The group that this group belongs to */
struct process *first; /* List of processes belonging to this group */
void *s_brk; /* Point where the stack is split */
void *between; /* Stack space between s_brk and up->s_brk */
};
#define group __grp__ /* Ignore this please */
#define highest_group __hgrp__
extern struct procgroup *group; /* Current running group */
extern struct procgroup *highest_group; /* highest group that has been seen
* while searching for a process
*/

52
lang/occam/lib/ocrt.c Normal file
View file

@ -0,0 +1,52 @@
/* ocrt.c - Occam runtime support */
#include "channel.h"
int chandes[]= { 0, 0, sizeof(int)+sizeof(long) };
int worddes[]= { 0, 0, sizeof(long) };
int bytedes[]= { 0, 0, 1 };
long any;
void catch(sig, file, line) int sig; char *file; int line;
/* Catches traps in the occam program */
{
register char *mes;
switch (sig) {
case 0:
mes="array bound error";
break;
case 6:
mes="division by zero";
break;
case 8:
mes="undefined variable";
break;
default:
return;
}
fprintf(stderr, "%s (%d) F: %s\n", file, line, mes);
abort();
}
chan file[_NFILE];
FILE *unix_file[_NFILE];
void initfile()
{
register i;
register chan *c=file;
for (i=0; i<_NFILE; i++) {
c->type=C_T_FILE;
c->f.flgs=0;
(c++)->f.index=i;
}
file[0].f.flgs|=C_F_INUSE;
unix_file[0]=stdin;
file[1].f.flgs|=C_F_INUSE;
unix_file[1]=stdout;
file[2].f.flgs|=C_F_INUSE;
unix_file[2]=stderr;
}

92
lang/occam/lib/par.c Normal file
View file

@ -0,0 +1,92 @@
/* par.c - Routines to simulate parallelism */
#include "process.h"
static void search_next(), DEADLOCK();
void resumenext()
/* Stops the current process, by saving its stack, and determines a new one
* to restart. In case the root of the process tree is passed more then once,
* without a process having done something useful, we'll have a deadlock.
*/
{
if (group!=nil) {
register struct process *proc= *group->active;
register wordsize size;
size=top_size(group->s_brk);
proc->stack=alloc((unsigned) size);
if (top_save(size, proc->stack)) {
group->active= &proc->next;
search_next();
} else {
free(proc->stack);
load_betweens();
}
} else
if (++deadlock>1) DEADLOCK();
}
static void search_next()
/* Tries to resume the active process, if this is not possible, the process
* tree will be searched for another process. If the process tree is fully
* traversed, search will restart at the root of the tree.
*/
{
while (*group->active==nil && group->up!=nil) {
save_between(group);
group=group->up;
group->active= &(*group->active)->next;
}
if (*group->active==nil) {
if (++deadlock>1) DEADLOCK();
group->active= &group->first;
}
highest_group=group;
while ((*group->active)->down!=nil) {
group=(*group->active)->down;
group->active= &group->first;
}
top_load((*group->active)->stack);
}
void parend()
/* Deletes the current process from its process group and searches for a new
* process to run. The entire group is removed if this is the last process in
* the group, execution then continues with the process that set up this group
* in the first place.
*/
{
register struct process *junk;
junk= *group->active;
*group->active=junk->next;
free((void *) junk);
if (group->first==nil) {
register struct procgroup *junk;
delete_between(group);
junk=group;
group=group->up;
free((void *) junk);
if (group!=nil)
(*group->active)->down=nil;
} else {
deadlock=0;
search_next();
}
}
static void DEADLOCK()
{
write(2, "DEADLOCK\n", 9);
abort();
}

53
lang/occam/lib/par_em2.e Normal file
View file

@ -0,0 +1,53 @@
mes 2,2,4
exp $top_size
pro $top_size, 14
ldl 0 ; s_brk
lor 1 ; s_brk SP
sbs 4 ; s_brk-SP
ret 4 ; return size of block to be saved
end 14
exp $top_save
pro $top_save, 0
loe 0
lde 4 ; load line number and file name
lim ; ignore mask
lor 0 ; LB
ldl 0 ; size of block
loc 4
loc 2
cuu
dup 2
stl 0 ; push & store size in 2 bytes
lor 1 ; SP (the SP BEFORE pushing)
lor 1 ; SP (address of stack top to save)
ldl 4 ; area
lol 0 ; size
bls 2 ; move whole block
asp 18 ; remove the lot from the stack
loc 1
ret 2 ; return 1
end 0
exp $top_load
pro $top_load, 0
ldl 0
dup 4
sde area ; copy area pointer from argument 0
loi 4 ; load indirect to
str 1 ; restore SP
lde area ; load area, note that the SP is now correct
lor 1 ; SP (the SP AFTER, see above)
lde area
lof 4 ; size of block
bls 2 ; move block back (SP becomes the SP BEFORE again!)
asp 2 ; drop size
str 0 ; LB
sim ; ignore mask
sde 4
ste 0 ; line and file
loc 0
ret 2 ; return 0
end 0
area
bss 4,0,0

46
lang/occam/lib/par_em4.e Normal file
View file

@ -0,0 +1,46 @@
mes 2,4,4
exp $top_size
pro $top_size, 20
lol 0 ; s_brk
lor 1 ; s_brk SP
sbs 4 ; s_brk-SP
ret 4 ; return size of block to be saved
end 20
exp $top_save
pro $top_save, 0
lde 0 ; load line number and file name
lim ; ignore mask
lor 0 ; LB
lol 0 ; size of block
lor 1 ; SP (the SP BEFORE pushing)
lor 1 ; SP (address of stack top to save)
lol 4 ; area
lol 0 ; size
bls 4 ; move whole block
asp 24 ; remove the lot from the stack
loc 1
ret 4 ; return 1
end 0
exp $top_load
pro $top_load, 0
lol 0
dup 4
ste area ; copy area pointer from argument 0
loi 4 ; load indirect to
str 1 ; restore sp
loe area ; load area, note that the SP is now correct
lor 1 ; SP (the SP AFTER, see above)
loe area
lof 4 ; size of block
bls 4 ; move block back (SP becomes the SP BEFORE again!)
asp 4 ; drop size
str 0 ; LB
sim ; ignore mask
sde 0 ; line and file
loc 0
ret 4 ; return 0
end 0
area
bss 4,0,0

51
lang/occam/lib/par_vax.s Normal file
View file

@ -0,0 +1,51 @@
# VAX code for the top_* primitives
.set BIG, 0x8000 # 32K chunk per movc3
.text
.align 1
.globl _top_size
.globl _top_save
.globl _top_load
_top_size: .word 0x0000
subl3 sp, 4(ap), r0 # bytes between stack pointer and break
addl2 $(8+6+1)*4, r0 # add 8 regs, 6 pushed longwords (line, file,
ret # ap, fp, size, sp) and 1 extra argument
_top_save: .word 0x0ff0 # save regs r4-r11
movq hol0, -(sp) # push line number and file name
movq ap, -(sp) # push LB equivalents ap and fp
pushl 4(ap) # push size
pushal -4(sp) # push sp (the sp AFTER pushing)
movl $BIG, r6 # chunk size in r6
movl 4(ap), r7 # size of block to move
movl sp, r1 # source address
movl 8(ap), r3 # destination address
cmpl r7, r6
jlequ 0f
1: movc3 r6, (r1), (r3) # move chunk of the block, add r6 to r1 and r3
subl2 r6, r7
cmpl r7, r6
jgtru 1b
0: movc3 r7, (r1), (r3) # move what's left
movl $1, r0 # return 1
ret
_top_load: .word 0x0000
movl 4(ap), r1 # source
movl (r1), sp # restore sp
movl $BIG, r6 # chunk size
movl 4(r1), r7 # size
movl sp, r3 # destination
cmpl r7, r6
jlequ 0f
1: movc3 r6, (r1), (r3) # move chunk of the block back
subl2 r6, r7
cmpl r7, r6
jgtru 1b
0: movc3 r7, (r1), (r3) # move what's left back
addl2 $8, sp # pop saved sp and size
movq (sp)+, ap # pop LB's
movq (sp)+, hol0 # pop line and file
clrl r0 # return 0
ret

130
lang/occam/lib/parco.c Normal file
View file

@ -0,0 +1,130 @@
/* parco.c - Common routines for simulating parallelism or coroutines on
* machines with downward growing stacks
*/
#include "process.h"
struct procgroup *group=nil, *highest_group;
int deadlock=0;
void pc_begin(s_brk, id)
register void *s_brk;
identification id;
/* Sets up a group of processes and puts the current process in it */
{
register struct procgroup *pg;
register struct process *p;
pg= (struct procgroup *) alloc(sizeof *pg);
p= (struct process *) alloc(sizeof *p);
pg->s_brk= s_brk==nil ? (void *) (&id +1) : s_brk;
pg->up=group;
pg->first=p;
pg->active= &pg->first;
p->next=nil;
p->down=nil;
p->id=id;
if (group!=nil)
(*group->active)->down=pg;
group=pg;
init_between(group);
}
int pc_fork(id) identification id;
/* Makes a copy of the stack top of the calling function and creates an
* entry for it in the current process group. Pc_fork() returns 1 in the
* current process, 0 in the copied process. The current process runs first.
*/
{
register struct process *newp;
register wordsize size;
newp= (struct process *) alloc(sizeof *newp);
newp->down=nil;
newp->id=id;
newp->next= *group->active;
*group->active= newp;
group->active= &newp->next;
size=top_size(group->s_brk);
newp->stack=alloc((unsigned) size);
if (top_save(size, newp->stack))
return 1;
else {
free(newp->stack);
load_betweens();
return 0;
}
}
void init_between(group) register struct procgroup *group;
/* Allocates memory to hold the stack space between s_brk and up->s_brk. */
{
register wordsize size;
if (group->up==nil
|| (size= (wordsize) group->up->s_brk - (wordsize) group->s_brk)==0)
group->between=nil;
else
group->between=alloc((unsigned) size);
}
void block_move();
void save_between(group) register struct procgroup *group;
/* Saves the stack space between s_brk and up->s_brk. */
{
register wordsize size;
if (group->between!=nil) {
size= (wordsize) group->up->s_brk - (wordsize) group->s_brk;
block_move(size, group->s_brk, group->between);
}
}
void load_betweens()
/* All stack pieces between s_brk and up->s_brk from the current group
* upto the 'highest_group' are loaded onto the stack at the right
* place (i.e. s_brk).
*/
{
register struct procgroup *gr=group, *up;
register wordsize size;
while (gr!=highest_group) {
up=gr->up;
if (gr->between!=nil) {
size= (wordsize) up->s_brk - (wordsize) gr->s_brk;
block_move(size, gr->between, gr->s_brk);
}
gr=up;
}
}
void delete_between(group) register struct procgroup *group;
/* Deallocates the stack space between s_brk and up->s_brk. */
{
if (group->between!=nil)
free(group->between);
}
void *malloc();
void *alloc(size) unsigned size;
{
register void *mem;
if ((mem=malloc(size))==nil) {
write(2, "Heap error\n", 14);
abort();
}
return mem;
}