finalization.scm 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. ;;; Finalization
  2. ;;; Copyright (C) 2024 David Thompson <dave@spritely.institute>
  3. ;;;
  4. ;;; Licensed under the Apache License, Version 2.0 (the "License");
  5. ;;; you may not use this file except in compliance with the License.
  6. ;;; You may obtain a copy of the License at
  7. ;;;
  8. ;;; http://www.apache.org/licenses/LICENSE-2.0
  9. ;;;
  10. ;;; Unless required by applicable law or agreed to in writing, software
  11. ;;; distributed under the License is distributed on an "AS IS" BASIS,
  12. ;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. ;;; See the License for the specific language governing permissions and
  14. ;;; limitations under the License.
  15. ;;; Commentary:
  16. ;;;
  17. ;;; JavaScript FinalizationRegistry emulation using guardians.
  18. ;;;
  19. ;;; Code:
  20. (define-module (hoot finalization)
  21. #:use-module (ice-9 match)
  22. #:use-module (srfi srfi-9)
  23. #:export (make-finalization-registry
  24. finalization-registry?
  25. finalization-registry-register!
  26. finalization-registry-unregister!
  27. poll-finalization-registry!))
  28. (define-record-type <finalization-registry>
  29. (%make-finalization-registry proc guardian cells unregister)
  30. finalization-registry?
  31. (proc finalization-registry-proc)
  32. (guardian finalization-registry-guardian)
  33. (cells finalization-registry-cells)
  34. (unregister finalization-registry-unregister))
  35. (define-record-type <finalization-cell>
  36. (make-finalization-cell held-value unregister-tokens registered?)
  37. finalization-cell?
  38. (held-value finalization-cell-held-value)
  39. (unregister-tokens finalization-cell-unregister-tokens
  40. set-finalization-cell-unregister-tokens!)
  41. (registered? finalization-cell-registered?
  42. set-finalization-cell-registered?!))
  43. (define (make-finalization-registry proc)
  44. (%make-finalization-registry proc
  45. (make-guardian)
  46. (make-hash-table)
  47. (make-hash-table)))
  48. (define* (finalization-registry-register! registry obj held-value
  49. #:optional unregister-token)
  50. (when (eq? obj held-value)
  51. (error "held value cannot be the same as target" obj held-value))
  52. (match registry
  53. (($ <finalization-registry> _ guardian cells unregister)
  54. (let* ((addr (object-address obj))
  55. (orig-cells (hashq-ref cells addr '()))
  56. (orig-unregister-cells (hashq-ref unregister unregister-token '())))
  57. (let lp ((cells* orig-cells))
  58. (match cells*
  59. ;; No existing cell for the held value, so add a new cell.
  60. (()
  61. (let ((cell (make-finalization-cell held-value
  62. (if unregister-token
  63. (list unregister-token)
  64. '())
  65. #t)))
  66. (hashv-set! cells addr (cons cell orig-cells))
  67. (when unregister-token
  68. (hashq-set! unregister unregister-token
  69. (cons cell orig-unregister-cells)))
  70. (guardian obj)))
  71. (((and cell ($ <finalization-cell> val tokens)) . rest)
  72. ;; If there is already a cell for the held value then we
  73. ;; don't need to add a new one.
  74. (if (eq? val held-value)
  75. ;; Add the unregister token to the set of tokens for
  76. ;; this cell.
  77. (when (and unregister-token
  78. (not (memq unregister-token tokens)))
  79. (set-finalization-cell-unregister-tokens!
  80. cell (cons unregister-token tokens))
  81. (hashq-set! unregister unregister-token
  82. (cons cell orig-unregister-cells)))
  83. (lp rest)))))))))
  84. (define (finalization-registry-unregister! registry unregister-token)
  85. (match registry
  86. (($ <finalization-registry> _ _ _ unregister)
  87. (match (hashq-ref unregister unregister-token)
  88. (#f #f)
  89. (tokens
  90. (for-each (lambda (cell)
  91. (set-finalization-cell-registered?! cell #f))
  92. tokens)
  93. (hashq-remove! unregister unregister-token)
  94. #t)))))
  95. (define (poll-finalization-registry! registry)
  96. (match registry
  97. (($ <finalization-registry> proc guardian cells unregister-tokens)
  98. (define (finalize cell)
  99. (define (cleanup-token token)
  100. (match (hashq-ref unregister-tokens token)
  101. (#f (values))
  102. (cells
  103. (match (delq cell cells)
  104. (()
  105. (hashq-remove! unregister-tokens token))
  106. (cells
  107. (hashq-set! unregister-tokens token cells))))))
  108. (match cell
  109. (($ <finalization-cell> held tokens registered?)
  110. (when registered?
  111. (proc held))
  112. (for-each cleanup-token tokens))))
  113. (let lp ()
  114. (match (guardian)
  115. (#f (values))
  116. (obj
  117. (let ((addr (object-address obj)))
  118. (for-each finalize (hashv-ref cells addr '()))
  119. (hashv-remove! cells addr)
  120. (lp))))))))