ffi/unsafe docs: fix example to work on CS

Closes #3833
This commit is contained in:
Matthew Flatt 2021-05-19 13:28:44 -06:00
parent 30cebe621a
commit b587fa15d3

View File

@ -415,44 +415,45 @@ complete solutions.
As an example for @racket[register-finalizer], As an example for @racket[register-finalizer],
suppose that you're dealing with a foreign function that returns a suppose that you're dealing with a foreign function that returns a
C string that you should free. Here is an attempt at creating a suitable type: C pointer that you should free, but you mostly want to use the memory
at a 16-byte offset. Here is an attempt at creating a suitable type:
@racketblock[ @racketblock[
(define @#,racketidfont{_sixteen-bytes/free} (define @#,racketidfont{_pointer-at-sixteen/free}
(make-ctype _pointer (make-ctype _pointer
#f (code:comment @#,t{a Racket bytes can be used as a pointer}) #f (code:comment @#,t{i.e., just @racket[_pointer] as an argument type})
(lambda (x) (lambda (x)
(let ([b (make-sized-byte-string x 16)]) (let ([p (ptr-add x 16)])
(register-finalizer x free) (register-finalizer x free)
b)))) p))))
] ]
The above code is wrong: the finalizer is registered for @racket[x], The above code is wrong: the finalizer is registered for @racket[x],
which is no longer needed after the byte string is created. Changing which is no longer needed after the new pointer @racket[p] is created. Changing
the example to register the finalizer for @racket[b] corrects the problem, the example to register the finalizer for @racket[p] corrects the problem,
but then @racket[free] is invoked @racket[b] it instead of on @racket[x]. but then @racket[free] is invoked @racket[p] instead of on @racket[x].
In the process of fixing this problem, we might be careful and log a message In the process of fixing this problem, we might be careful and log a message
for debugging: for debugging:
@racketblock[ @racketblock[
(define @#,racketidfont{_sixteen-bytes/free} (define @#,racketidfont{_pointer-at-sixteen/free}
(make-ctype _pointer (make-ctype _pointer
#f (code:comment @#,t{a Racket bytes can be used as a pointer}) #f
(lambda (x) (lambda (x)
(let ([b (make-sized-byte-string x 16)]) (let ([p (ptr-add x 16)])
(register-finalizer b (register-finalizer p
(lambda (ignored) (lambda (ignored)
(log-debug (format "Releasing ~s\n" b)) (log-debug (format "Releasing ~s\n" p))
(free x))) (free x)))
b)))) p))))
] ]
Now, we never see any logged event. The problem is that the finalizer is a Now, we never see any logged event. The problem is that the finalizer is a
closure that keeps a reference to @racket[b]. Instead of referencing the closure that keeps a reference to @racket[p]. Instead of referencing the
value that is finalized, use the input argument to the finalizer; simply changing value that is finalized, use the input argument to the finalizer; simply changing
@racket[ignored] to @racket[b] above solves the problem. (Removing the @racket[ignored] to @racket[p] above solves the problem. (Removing the
debugging message also avoids the problem, since the finalization debugging message also avoids the problem, since the finalization
procedure would then not close over @racket[b].)} procedure would then not close over @racket[p].)}
@deftogether[( @deftogether[(