db: fix finalization problems

A `this%` expression used in a finalization callback implicitly
referred to `this`, since it's a dynamic reference to the object's
class. As a result, the finalizer for `this` refers to `this`, so
`this` never becomes collectable. The problem is fixed by
lifting the `this%` out of the `lambda`.

Less significantly, the finalizer thread in "prepared.rkt" captured
various parameters on creation, including the current namespace. If a
prepared statement is bound to a module-level variable, then the
finalizer thread refers through the namespace to the prepared
statement, so the prepared statement can never be finalized. Setting
the current namespace to a fresh empty one while creating the thread
avoids that specific problem. (Other parameters could cause similar
problems, but solving the namespace one works well enough for now.)
This commit is contained in:
Matthew Flatt 2015-01-20 13:04:23 -07:00
parent 857950a2b2
commit 5a8a0aff02
2 changed files with 27 additions and 24 deletions

View File

@ -102,6 +102,9 @@
(define will-executor (make-will-executor))
(define finalizer-thread
;; Set parameters for the new thread to avoid references to the
;; namespace, since prepared statements may be bound to globals, etc.
(parameterize ([current-namespace (make-empty-namespace)])
(thread/suspend-to-kill
(lambda ()
(let loop ()
@ -116,4 +119,4 @@
"prepared statement finalizer thread handled non-exception"])
e))])
(will-execute will-executor))
(loop)))))
(loop))))))

View File

@ -374,13 +374,13 @@
(handle-status* who full-s -db db-spec pst))
;; ----
(super-new)
(register-finalizer-and-custodian-shutdown this
(lambda (obj)
(register-finalizer-and-custodian-shutdown
this
;; Keep a reference to the class to keep all FFI callout objects
;; (eg, sqlite3_close) used by its methods from being finalized.
(let ([dont-gc this%])
(lambda (obj)
(send obj disconnect)
;; Dummy result to prevent reference from being optimized away
dont-gc)))))