|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T d
Length: 15318 (0x3bd6)
Types: TextFile
Names: »daemon.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/gnu-31mar87/scheme/microcode/daemon.c«
/* Hey EMACS, this is -*- C -*- code! */
/****************************************************************
* *
* Copyright (c) 1984 *
* Massachusetts Institute of Technology *
* *
* This material was developed by the Scheme project at the *
* Massachusetts Institute of Technology, Department of *
* Electrical Engineering and Computer Science. Permission to *
* copy this software, to redistribute it, and to use it for any *
* purpose is granted, subject to the following restrictions and *
* understandings. *
* *
* 1. Any copy made of this software must include this copyright *
* notice in full. *
* *
* 2. Users of this software agree to make their best efforts (a)*
* to return to the MIT Scheme project any improvements or *
* extensions that they make, so that these may be included in *
* future releases; and (b) to inform MIT of noteworthy uses of *
* this software. *
* *
* 3. All materials developed as a consequence of the use of *
* this software shall duly acknowledge such use, in accordance *
* with the usual standards of acknowledging credit in academic *
* research. *
* *
* 4. MIT has made no warrantee or representation that the *
* operation of this software will be error-free, and MIT is *
* under no obligation to provide any services, by way of *
* maintenance, update, or otherwise. *
* *
* 5. In conjunction with products arising from the use of this *
* material, there shall be no use of the name of the *
* Massachusetts Institute of Technology nor of any adaptation *
* thereof in any advertising, promotional, or sales literature *
* without prior written consent from MIT in each case. *
* *
****************************************************************/
\f
/* File: DAEMON.C
This file contains code for the Garbage Collection daemons.
There are currently two daemons, one for closing files which
have disappeared due to GC, the other for supporting object
hash tables where entries disappear when the corresponding
object is released due to GC.
*/
#include "scheme.h"
#include "primitive.h"
\f
/* Hash Tables
The hash table support here allows the Scheme runtime system to
support "populations." A population is conceptually a set of
items, but with the special property that an item remains in the
population only as long as the object would remain in the system
were it not in the set. That is, an item is removed from all
populations it belongs to when a garbage collection removes the
item from the system.
The actual support provided is a pair of hash tables. An object
can be hashed to yield the current value of a constantly
incrementing counter. The hash table is constructed by hashing on
the address of the object, and both the item and the unique number
assigned to it are stored in the table. The unhash table is
constructed by hashing on the unique number and again storing both
the item and its unique number. Both the hash and unhash tables
appear to the user to be vectors, but they have a NON_MARKED header
so that the ordinary GC will not update pointers located within
them.
At every GC flip (i.e. after all objects have been moved from old
space to new space, but before the Scheme code runs again), the
Rehash Daemon is called. It goes through the hash table (all of
which points into old space) and reconstructs it. Whenever it
finds a non-pointer object or an object which points at a BROKEN
HEART (i.e. one which the GC copied into new space) it rehashes the
new address and adds it to the new table.
Thus, the hash tables provide a mapping from objects to unique
numbers, with the additional property that the table does not
retain objects that the garbage collector would otherwise release
from the system.
*/
#define Hash_It(P) \
(((Address(P)>>16)&0xFF)+ \
((Address(P)>>8)&0xFF)+ \
(Address(P) & 0xFF))
Pointer The_Hash_Table, The_Unhash_Table;
long HASH_TABLE_SIZE;
\f
/* (INITIALIZE-OBJECT-HASH FIXNUM)
[Primitive number 0x8A]
Resets the unique ID generator used in the 2-dimensional hash
tables which implement properties and populations. The value of
FIXNUM will be used for the next object put into the hash
tables.
*/
Built_In_Primitive(Prim_Initialize_Object_Hash, 1, "INITIALIZE-OBJECT-HASH")
{ fast long i;
long HASH_TABLE_SIZE, Length;
Primitive_1_Arg();
Arg_1_Type(TC_FIXNUM);
HASH_TABLE_SIZE = Get_Integer(Arg1);
Length = 8 + (2 * HASH_TABLE_SIZE);
if (!Test_Pure_Space_Top(Free_Constant + Length))
{ Update_FObj_Slot(Hash_Table, NIL);
Update_FObj_Slot(Unhash_Table, NIL);
return NIL;
}
/* Make a Constant/Pure block to hold the two vectors */
/* Constant part header */
*Free_Constant++ = Make_Non_Pointer(TC_MANIFEST_SPECIAL_NM_VECTOR, Length-3);
*Free_Constant++ = Make_Non_Pointer(PURE_PART, Length-1);
/* Constant part contains hash and unhash tables */
Update_FObj_Slot(Hash_Table, Make_Pointer(TC_VECTOR, Free_Constant));
*Free_Constant++ = Make_Non_Pointer(TC_MANIFEST_VECTOR, HASH_TABLE_SIZE);
for (i=0; i < HASH_TABLE_SIZE; i++) *Free_Constant++ = NIL;
Update_FObj_Slot(Unhash_Table, Make_Pointer(TC_VECTOR, Free_Constant));
*Free_Constant++ = Make_Non_Pointer(TC_MANIFEST_VECTOR, HASH_TABLE_SIZE);
for (i=0; i < HASH_TABLE_SIZE; i++) *Free_Constant++ = NIL;
/* Pure part header */
*Free_Constant++ = Make_Non_Pointer(TC_MANIFEST_SPECIAL_NM_VECTOR, 1);
*Free_Constant++ = Make_Non_Pointer(CONSTANT_PART, Length-3);
/* Block trailer */
*Free_Constant++ = Make_Non_Pointer(TC_MANIFEST_SPECIAL_NM_VECTOR, 1);
*Free_Constant++ = Make_Non_Pointer(END_OF_BLOCK, Length-1);
Update_FObj_Slot(Hash_Number, FIXNUM_0);
Set_Pure_Top(Free_Constant);
return NIL;
}
\f
Pointer Hash_One_Object(Object, New_Unique_ID, Update_UID_Count)
Pointer Object, New_Unique_ID;
Boolean Update_UID_Count;
{ Pointer Bucket;
long Hash, Obj_Hash;
Obj_Hash = Hash_It(Object) % HASH_TABLE_SIZE + 1;
Bucket = Vector_Ref(The_Hash_Table, Obj_Hash);
while (Type_Code(Bucket) == TC_LIST)
{ Pointer This_Entry;
This_Entry = Vector_Ref(Bucket, CONS_CAR);
if (Vector_Ref(This_Entry, CONS_CAR) == Object)
return Vector_Ref(This_Entry, CONS_CDR);
Bucket = Vector_Ref(Bucket, CONS_CDR);
}
Primitive_GC_If_Needed(Free + 6);
Hash = Hash_It(New_Unique_ID) % HASH_TABLE_SIZE + 1;
Free[CONS_CAR] = Make_Pointer(TC_LIST, Free+2);
Free[CONS_CDR] = Vector_Ref(The_Hash_Table, Obj_Hash);
Vector_Set(The_Hash_Table, Obj_Hash, Make_Pointer(TC_LIST, Free));
Free += 2;
Free[CONS_CAR] = Object;
Free[CONS_CDR] = New_Unique_ID;
Free += 2;
Free[CONS_CAR] = Make_Pointer(TC_LIST, Free-2);
Free[CONS_CDR] = Vector_Ref(The_Unhash_Table, Hash);
Vector_Set(The_Unhash_Table, Hash, Make_Pointer(TC_LIST, Free));
Free += 2;
if (Update_UID_Count)
Update_FObj_Slot(Hash_Number, FIXNUM_0+1+Get_Integer(New_Unique_ID));
return New_Unique_ID;
}
\f
/* (OBJECT-HASH OBJECT)
[Primitive number 0x5A]
Returns the unique hash number associated with OBJECT. This is
used in the implementation of property lists and populations.
*/
Built_In_Primitive(Prim_Object_Hash, 1, "OBJECT-HASH")
{ Pointer Result;
Primitive_1_Arg();
The_Hash_Table = Get_Fixed_Obj_Slot(Hash_Table);
The_Unhash_Table = Get_Fixed_Obj_Slot(Unhash_Table);
if (The_Hash_Table==NIL) Primitive_Error(ERR_NO_HASH_TABLE);
HASH_TABLE_SIZE = Vector_Length(The_Unhash_Table);
return Hash_One_Object(Arg1, Get_Fixed_Obj_Slot(Hash_Number), true);
}
\f
/* (OBJECT_UNHASH NUMBER)
[Primitive number 0x5B]
Returns the object associated with a hash number (ie the inverse
operation of OBJECT_HASH). Returns NIL if there is no
associated object (which will occur if no object was ever hashed
to this value, or if that object has been removed by a garbage
collection, since these hash table are explicitly built in order
NOT to retain objects which would otherwise disappear.)
*/
Built_In_Primitive(Prim_Object_Unhash, 1, "OBJECT-UNHASH")
{ long Hash;
Pointer Hash_Arg, Bucket;
Primitive_1_Arg();
Arg_1_Type(TC_FIXNUM);
The_Unhash_Table = Get_Fixed_Obj_Slot(Unhash_Table);
if (The_Unhash_Table==NIL) Primitive_Error(ERR_NO_HASH_TABLE);
HASH_TABLE_SIZE = Vector_Length(The_Unhash_Table);
Hash = Hash_It(Arg1) % HASH_TABLE_SIZE + 1;
Bucket = Vector_Ref(The_Unhash_Table, Hash);
while (Type_Code(Bucket) == TC_LIST)
{ Pointer Entry;
Entry = Vector_Ref(Bucket, CONS_CAR);
if (Arg1 == Vector_Ref(Entry, CONS_CDR))
return Vector_Ref(Entry, CONS_CAR);
Bucket = Vector_Ref(Bucket, CONS_CDR);
}
return NIL;
}
\f
/* (REHASH_GC_DAEMON)
[Primitive number 0x5C]
Used only immediately after a GC, this primitive creates a new
pair of hash tables for use with the property list and
population mechanisms. It depends on the broken hearts left by
the previous GC.
*/
Built_In_Primitive(Prim_Rehash_Gc_Daemon, 0, "REHASH-GC-DAEMON")
{ long i;
Pointer Chain, Chain_End;
Primitive_0_Args();
The_Hash_Table = Get_Fixed_Obj_Slot(Hash_Table);
The_Unhash_Table = Get_Fixed_Obj_Slot(Unhash_Table);
if (The_Hash_Table==NIL) return NIL;
HASH_TABLE_SIZE = Vector_Length(The_Unhash_Table);
Chain = NIL;
Chain_End = NIL;
/* Create a single chain of all the entries from the hash table ...
clear both the hash and unhash tables on the way */
for (i=1; i <= HASH_TABLE_SIZE; i++)
{ Pointer Bucket;
Fast_Vector_Set(The_Unhash_Table, i, NIL);
Bucket = Fast_Vector_Ref(The_Hash_Table, i);
if (Bucket != NIL)
{ if (Chain==NIL) Chain = Bucket;
else Fast_Vector_Set(Chain_End, CONS_CDR, Bucket);
while (Fast_Vector_Ref(Bucket, CONS_CDR) != NIL)
Bucket = Fast_Vector_Ref(Bucket, CONS_CDR);
Chain_End = Bucket;
Fast_Vector_Set(The_Hash_Table, i, NIL);
}
}
/* Prim_Rehash_Gc_Daemon continues on the next page */
\f
/* Prim_Rehash_Gc_Daemon, continued */
/* Walk the chain rehashing entries that have been relocated */
while (Chain != NIL)
{ Pointer Object, New_Object, Entry, New_Hash;
long Result;
Entry = Fast_Vector_Ref(Chain, CONS_CAR);
Chain = Fast_Vector_Ref(Chain, CONS_CDR);
Object = Fast_Vector_Ref(Entry, CONS_CAR);
if (GC_Type_Non_Pointer(Object)) New_Object = Object;
else if (Get_Pointer(Object) > Constant_Space) New_Object = Object;
else if (Type_Code(Fast_Vector_Ref(Object, 0))==TC_BROKEN_HEART)
New_Object = Make_New_Pointer(Type_Code(Object),
Fast_Vector_Ref(Object, 0));
else continue;
Hash_One_Object(New_Object, Fast_Vector_Ref(Entry, CONS_CDR), false);
}
return TRUTH;
}
\f
/* The format of the open files vector is:
|----------------|--------|
|MANIFEST_VECTOR | n |
|----------------|--------|.
|FIXNUM | m | . n = length of the vector
|----------------|--------| | m = count of used slots
Lock |NULL or NM_VECT | n-2 | |
.|----------------|--------| | HUNK3s are formatted:
. |HUNK3 | ----------> |--------------------|
| |----------------|--------| | | Channel number |
| |HUNK3 | | | |--------------------|
| |----------------|--------| | | File Name |
m < |HUNK3 | | > n |--------------------|
| |----------------|--------| | | Input or Output |
| |HUNK3 | | | |--------------------|
| |----------------|--------| |
. | ... | | | If the type code of Lock
.|----------------|--------| | is NULL, then the vector
| ---UNUSED--- | | | is in use by SCHEME and
|----------------|--------| | cannot be accessed here.
| ... | | .
|----------------|--------|.
*/
#define OPEN_FILES_COUNT 1
#define OPEN_FILES_INTERLOCK 2
#define OPEN_FILES_FIRST_FILE 3
#define FILE_CHANNEL 0
#define FILE_NAME 1
#define FILE_IN_OR_OUT 2
\f
/* (CLOSE_LOST_OPEN_FILES)
[Primitive number 0xC7]
This primitive can ONLY be called as one of the GC daemons. It
is responsible for closing and releasing any files which have
"disappeared" due to a garbage collection. It relies on the
broken hearts left behind by the GC to do its work.
*/
Built_In_Primitive(Prim_Close_Lost_Open_Files, 0, "CLOSE-LOST-OPEN-FILES")
{ Pointer Open_Files_Vector, *From_File, *To_File;
long i, NFiles, Orig_Count;
Primitive_0_Args();
/* Close_Lost_Open_Files walks down the used entries of the
Open Files Vector. For each entry, it either relocates it (if
the Garbage Collector provided a forwarding address) or it closes
the file and removes the entry from the vector.
*/
Open_Files_Vector = Get_Fixed_Obj_Slot(Open_Files);
if ((Open_Files_Vector==NIL) ||
(Type_Code(Vector_Ref(Open_Files_Vector,
OPEN_FILES_INTERLOCK)) ==
TC_NULL)) return NIL;
Orig_Count = Get_Integer(Vector_Ref(Open_Files_Vector,
OPEN_FILES_COUNT));
NFiles = Orig_Count;
To_File = Nth_Vector_Loc(Open_Files_Vector, OPEN_FILES_FIRST_FILE);
/* Prim_Close_Lost_Open_Files continues on next page */
\f
/* Prim_Close_Lost_Open_Files, continued */
for (i=0, From_File=To_File; i < Orig_Count; i++, From_File++)
{ if (Type_Code(*Get_Pointer(*From_File))==TC_BROKEN_HEART)
{ /* The file block (hunk3) has been moved by the GC which just
ended. Relocate the pointer in the Open Files Vector. */
Store_Address(*To_File, Address(*Get_Pointer(*From_File)));
To_File += 1;
}
else
{ if (Get_Pointer(*From_File) > Constant_Space)
{ Store_Address(*To_File, Address(*From_File));
To_File += 1;
}
else
{ /* The file is no longer accessible, since its file block
was not relocated by the GC. Close the file and shrink the
Open Files Vector */
long File_Number;
File_Number = Get_Integer(Vector_Ref(*From_File, FILE_CHANNEL));
fclose(Channels[File_Number]);
Channels[File_Number] = NULL;
NFiles -= 1;
}
}
}
for (i=NFiles; i < Orig_Count; i++) *To_File++ = NIL;
Vector_Set(Open_Files_Vector, OPEN_FILES_COUNT, FIXNUM_0+NFiles);
return TRUTH;
}