nil
.
One of the uses of weak references is to manage Lisp items that
represent operating system resources. It is often useful to allow the
internal representation of the resource to have a reference to the
Lisp item representing the resource, but using a standard strong
reference would prevent garbage collection of the Lisp representation
even if it is not reachable from the Lisp side. Just saving the
address of the Lisp pointer without registering it for GC-protection
would avoid this problem but would then result in a dangling pointer
since the internal representation has no way of knowing whether the
pointer is valid or not. By using a weak reference, the internal
representation can access the Lisp representation while it is still
available, but will see nil
as the reference once the
representation has been garbage-collected.
make-weak-box
takes one argument, the reference item, and returns
a new weak box. The accessor function weak-box-value
takes a weak
box and returns the contents of its reference cell, either the item
passed to the constructor or nil
if the item has been garbage
collected.
These two functions should be accessed as exported functions in the
system
package.
> (setf b1 (system:make-weak-box (list 1))) #<Weak Box: #4004e458> > (setf b2 (system:make-weak-box (list 2))) #<Weak Box: #4004f608> > (system:weak-box-value b1) (1) > (system:weak-box-value b2) (2)
Next, save the value in b2
in x
, thus establishing a strong
reference, clear the history variables, and do a gc
. Then
b1
's weak reference has been cleared, but because of the strong
reference to the contents of b2
through x
, b2
's reference
has not been cleared.
> (setf x (system:weak-box-value b2)) (2) > (setf * nil ** nil *** nil) NIL (2) > (gc) NIL > (system:weak-box-value b1) NIL > (system:weak-box-value b2) (2)
After clearing the history variables and x
and running a gc
,
the contents of b2
has also been cleared:
> (setf * nil ** nil *** nil x nil) NIL > (gc) NIL > (system:weak-box-value b2) NIL
Next, we can again create some weak boxes but this time save them in a
workspace. Again the contents of b2
is also assigned to x
and
the history variables are cleared.
> (setf b1 (system:make-weak-box (list 1))) #<Weak Box: #400510a8> > (setf b2 (system:make-weak-box (list 2))) #<Weak Box: #4004c0d8> > (setf x (system:weak-box-value b2)) (2) > (setf * nil ** nil *** nil) NIL > (save-workspace "fred.wks")
A gc
is executed prior to creating the saved workspace. Therefore
in a new process started from the saved workspace the contents of
b1
has been cleared but, because of the strong reference through
x
, the contents of b2
has not.
nokomis% xlisp -wfred.wks XLISP-PLUS version 3.04 Portions Copyright (c) 1988, by David Betz. Modified by Thomas Almy and others. XLISP-STAT Release 3.52.13 (Beta). Copyright (c) 1989-1999, by Luke Tierney. > b1 #<Weak Box: #400510a0> > b2 #<Weak Box: #4004c0d0> > x (2) > (weak-box-value b1) NIL > (weak-box-value b2) (2)
CONS
cells. Their car
contains
the referenced item, and their cdr
is used to place them on a list
of weak boxes. At the end of a collection the list is processed.
Unreachable boxes are removed and reachable boxes with unreachable
contents are cleared. To allow weak boxes to be maintained in saved
workspaces, the head of the weak box list is written out at the
beginning of a save and read in at the beginning of a restore.
The patch file should work against 3.52.13, though I haven't tried it yet to make sure.
<weakbox.patch>= <xldmem.h
changes> <xldmem.c
changes> <xlftab.h
changes> <xlftab.c
changes> <xlglob.h
changes> <xlglob.c
changes> <xlimage.c
changes> <xlprin.c
changes> <xlinit.c
changes> <xlsys.c
changes>
xlglob.c
and xlglob.h
xlweakboxes
is added to hold the list of weak
boxes. It is statically initialized to nil
so the GC works during
workspace initialization. A global a_weakbox
for the type symbol
is also added.
<xlglob.h
changes>= (<-U)
Index: xlglob.h
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlglob.h,v
retrieving revision 1.19
diff -r1.19 xlglob.h
115a116
> XLGLOBAL LVAL a_weakbox;
206a208,210
>
> /* Weak box list */
> XLGLOBAL LVAL xlweakboxes;
Definesa_weakbox
,xlweakboxes
(links are to index).
<xlglob.c
changes>= (<-U)
Index: xlglob.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlglob.c,v
retrieving revision 1.22
diff -r1.22 xlglob.c
144a145
> LVAL a_weakbox=NULL;
195a197,199
>
> /* Weak box list */
> LVAL xlweakboxes = NIL;
Definesa_weakbox
,xlweakboxes
(links are to index).
xldmem.h
WEAKBOX
type is added. To make room for a few more additions,
CONS
is shifted down to 16, along with the other CONS
-like
types.
<xldmem.h
changes>= (<-U)
Index: xldmem.h
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xldmem.h,v
retrieving revision 1.16
diff -r1.16 xldmem.h
265a266
> #define WEAKBOX 13
268,269c269,270
< #define CONS 13
< #define COMPLEX 14
---
> #define CONS 16
> #define COMPLEX 17
271c272
< #define RATIO 15
---
> #define RATIO 18
273,275c274,276
< #define USTREAM 16
< #define DARRAY 17
< #define RNDSTATE 18
---
> #define USTREAM 19
> #define DARRAY 20
> #define RNDSTATE 21
277c278
< #define BCCLOSURE 19
---
> #define BCCLOSURE 22
DefinesWEAKBOX
(links are to index).
xlftab.c
and xlftab.h
<xlftab.c
changes>= (<-U)
Index: xlftab.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlftab.c,v
retrieving revision 1.37
diff -r1.37 xlftab.c
312a313,314
> { "MAKE-WEAK-BOX", S, xmkweakbox },
> { "WEAK-BOX-VALUE", S, xweakboxval },
865,866d866
< { NULL, S, xnotimp },
< { NULL, S, xnotimp },
Definesmake-weak-box
,weak-box-value
(links are to index).
<xlftab.h
changes>= (<-U)
Index: xlftab.h
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlftab.h,v
retrieving revision 1.32
diff -r1.32 xlftab.h
125c125,126
< xgc(V),xexpand(V),xalloc(V),xmem(V),xregfinal(V);
---
> xgc(V),xexpand(V),xalloc(V),xmem(V),xregfinal(V),
> xmkweakbox(V),xweakboxval(V);
Definesmake-weak-box
,weak-box-value
(links are to index).
xlimage.c
xlweakboxes
is written out
and read in at the beginning of the save/restore process. Weak boxes
are treated like CONS
cells during reading and writing of saved
images, i.e. both car
and cdr
are followed.
<xlimage.c
changes>= (<-U)
Index: xlimage.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlimage.c,v
retrieving revision 1.22
diff -r1.22 xlimage.c
110a111,113
> /* write out the weak box list head */
> writeptr(cvoptr(xlweakboxes));
>
132a136
> case WEAKBOX:
321a326,328
> /* read in the weak box list head */
> xlweakboxes = cviptr(readptr());
>
340a348
> case WEAKBOX:
xldmem.c
CONS
cells. Their car
contains
the referenced item, and their cdr
is used to place them on the
xlweakboxes
list.
<xmkweakbox
definition>= (U->)
> LVAL xmkweakbox(V)
> {
> LVAL val = xlgetarg();
> LVAL box = consa(val);
> xllastarg();
> ntype(box) = WEAKBOX;
> Rplacd(box, xlweakboxes);
> xlweakboxes = box;
> return box;
> }
Definesxmkweakbox
(links are to index).
Right after identifying and forwarding finalizers, weak boxes are
processed by traversing the list of weak boxes xlweakboxes
.
Unreachable boxes are removed, and reachable boxes with unreachable
contents are cleared.
<check_weak_boxes
definition>= (U->)
> static void check_weak_boxes()
> {
> LVAL last = NIL, next = xlweakboxes;
>
> while (ntype(next) == WEAKBOX) {
> if (is_new_node(next)) {
> /* delete box from weak box list */
> LVAL tail = cdr(next);
> if (null(last))
> xlweakboxes = tail;
> else
> Rplacd(last, tail);
> next = tail;
> }
> else {
> if (is_new_node(car(next)))
> Rplaca(next, NIL);
> last = next;
> next = cdr(next);
> }
> }
> }
Definescheck_weak_boxes
(links are to index).
In forwarding, weak boxes are treated as non-allocated with no children.
In other words, neither their car
nor cdr
are followed.
<weak box forwarding information>= (U->) > type_info[WEAKBOX].allocated = FALSE; > type_info[WEAKBOX].has_children = FALSE;
The marking code for the mark-and-sweep collector should work as is with weak boxes, but I haven't thought it through. However, the post-processing of the weak box list (or the finalization stuff for that matter) is not included in this collector, so it currently won't work with weak boxes or finalization.
The accessor just returns the car
value. Two macros are defined
here since this is the only place they are used; they should really
be in xlisp.h
.
<xweakboxval
definition>= (U->)
> #define weakboxp(x) (ntype(x) == WEAKBOX)
> #define xlgaweakbox() (testarg(typearg(weakboxp)))
> LVAL xweakboxval(V)
> {
> LVAL box = xlgaweakbox();
> xllastarg();
> return car(box);
Definesxweakboxval
(links are to index).
Here are the rest of the changes:
<xldmem.c
changes>= (<-U) Index: xldmem.c =================================================================== RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xldmem.c,v retrieving revision 1.32 diff -r1.32 xldmem.c 12a13 > static VOID check_weak_boxes(V); 223a225,226 <weak box forwarding information> 1130a1134 > check_weak_boxes(); 2938a2943,2965 <check_weak_boxes
definition> > 2946a2974,2993 > } > <xmkweakbox
definition> > <xweakboxval
definition>
xlprin.c
#.(make-weak-box x)
.
<xlprin.c
changes>= (<-U)
Index: xlprin.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlprin.c,v
retrieving revision 1.33
diff -r1.33 xlprin.c
562a563,569
> case WEAKBOX:
> #ifdef PRINTCIRCLE
> if (checkcircle(fptr, vptr)) break;
> #endif /* PRINTCIRCLE */
> checkreadable(vptr);
> putatm(fptr,"Weak Box",vptr);
> break;
xlinit.c
a_weakbox
is initialized in xlinit.c
.
<xlinit.c
changes>= (<-U)
Index: xlinit.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlinit.c,v
retrieving revision 1.33
diff -r1.33 xlinit.c
555a556
> a_weakbox = xlenter("SYSTEM:WEAK-BOX");
xlsys.c
type-of
and typep
functions have handlers for the
weak box type added to them.
<xlsys.c
changes>= (<-U)
Index: xlsys.c
===================================================================
RCS file: /NOKOMIS/users/luke/SRC/xlispstat/xlsys.c,v
retrieving revision 1.24
diff -r1.24 xlsys.c
131a132
> case WEAKBOX: return (a_weakbox);
175a177
> if (arg == a_weakbox) return WEAKBOX;
check_weak_boxes
definition>: D1, U2
xldmem.c
changes>: U1, D2
xldmem.h
changes>: U1, D2
xlftab.c
changes>: U1, D2
xlftab.h
changes>: U1, D2
xlglob.c
changes>: U1, D2
xlglob.h
changes>: U1, D2
xlimage.c
changes>: U1, D2
xlinit.c
changes>: U1, D2
xlprin.c
changes>: U1, D2
xlsys.c
changes>: U1, D2
xmkweakbox
definition>: D1, U2
xweakboxval
definition>: D1, U2