Initial implementation and readme

This commit is contained in:
Vivianne 2024-09-11 21:59:02 -04:00
commit c87e4f74f0
3 changed files with 87 additions and 0 deletions

22
README.org Normal file
View file

@ -0,0 +1,22 @@
#+TITLE:Guile Weak References
This is a simple little library that makes use of the weak hash table functionality in core Guile
to hackily implement weak references, objects which weakly point to other objects.
The interface is very simple:
(make-weakref obj)
- Create a weak reference, which will not count as a reference, so the object can be garbage collected
(weakref-object obj)
- Get the object, or #f if it has been garbage collected.
* Implementation
This is a very simple implementation. There's a globally incremented ID associated with each object, and
the object is stored in two global weak hash tables, one id-to-object and another object-to-id. The weak
reference is a record type that stores the ID and also a tombstone property to cache whether the object
has been collected to avoid the need to look it up after that point.
I created this because I needed an interface outside of the hash tables to refer to single objects instead
of creating a bunch of weak dictionaries. Also, Guile Hoot does not yet support iteration through weak key
hash tables, so this will allow me to create a list of weak refs to iterate over instead.

25
tests/weakref.scm Normal file
View file

@ -0,0 +1,25 @@
(define-module (tests weakref)
#:use-module (weakref)
#:use-module (srfi srfi-64))
(test-begin "tests-weakref")
(define a (cons 'foo 'bar))
(define refa (make-weakref a))
(define refb (make-weakref a))
(define b (cons 'foo 'bar))
(define refc (make-weakref b))
(test-assert a (weakref-object refa))
(test-assert a (weakref-object refb))
(test-assert b (weakref-object refc))
(format #t "~s\n" refc)
(test-eq (weakref-object refa) (weakref-object refb))
(test-assert (not (eq? (weakref-object refb) (weakref-object refc))))
;; no way to reliably force GC for a test I'm afraid
(test-end "tests-weakref")

40
weakref.scm Normal file
View file

@ -0,0 +1,40 @@
(define-module (weakref)
#:use-module (srfi srfi-9)
#:use-module (srfi srfi-9 gnu)
#:use-module (srfi srfi-64)
#:export (make-weakref
weakref?
weakref-object))
(define *next-id* 0)
(define *ids-to-objects* (make-weak-value-hash-table))
(define *objects-to-ids* (make-weak-key-hash-table))
(define-record-type <weakref>
(_make-weakref id tombstone)
weakref?
(id weakref-id)
(tombstone weakref-tombstone set-weakref-tombstone!))
(set-record-type-printer!
<weakref>
(lambda (ref port)
(format port "#<weakref: ~a id=~a>" (or (weakref-object ref) "[gone]") (weakref-id ref))))
(define (make-weakref obj)
(_make-weakref
(or (hashq-ref *objects-to-ids* obj)
(let ((id *next-id*))
(set! *next-id* (1+ *next-id*))
(hashq-set! *ids-to-objects* id obj)
(hashq-set! *objects-to-ids* obj id)
id))
#f))
(define (weakref-object ref)
(if (weakref-tombstone ref)
#f
(or (hashq-ref *ids-to-objects* (weakref-id ref))
(begin
(set-weakref-tombstone! ref #t)
#f))))