db: fixed issues with statement finalization (don't use weak hash)

In some cases, statements were disappearing from statement-table
without being finalized; this makes disconnect fail.
(I was only able to produce the problem when the db lib was
instantiated in a sub custodian that is later shutdown....
like the way the rackunit gui runs the test suite.)
This commit is contained in:
Ryan Culpepper 2011-08-29 03:45:35 -06:00
parent 397702808a
commit 418985d4c4
4 changed files with 42 additions and 27 deletions

View File

@ -90,7 +90,6 @@
;; extension hooks: usually shouldn't need to override ;; extension hooks: usually shouldn't need to override
finalize ;; -> void finalize ;; -> void
register-finalizer ;; -> void
;; inspection only ;; inspection only
get-param-types ;; -> (listof TypeDesc) get-param-types ;; -> (listof TypeDesc)

View File

@ -3,6 +3,7 @@
racket/list racket/list
racket/math racket/math
ffi/unsafe ffi/unsafe
ffi/unsafe/atomic
"../generic/interfaces.rkt" "../generic/interfaces.rkt"
"../generic/prepared.rkt" "../generic/prepared.rkt"
"../generic/sql-data.rkt" "../generic/sql-data.rkt"
@ -24,7 +25,7 @@
char-mode) char-mode)
(init strict-parameter-types?) (init strict-parameter-types?)
(define statement-table (make-weak-hasheq)) (define statement-table (make-hasheq))
(define lock (make-semaphore 1)) (define lock (make-semaphore 1))
(define use-describe-param? (define use-describe-param?
@ -437,13 +438,14 @@
(define/public (disconnect) (define/public (disconnect)
(define (go) (define (go)
(start-atomic)
(let ([db* db] (let ([db* db]
[env* env]) [env* env])
(when db*
(let ([statements (hash-map statement-table (lambda (k v) k))])
(set! db #f) (set! db #f)
(set! env #f) (set! env #f)
(set! statement-table #f) (end-atomic)
(when db*
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)]) (for ([pst (in-list statements)])
(free-statement* 'disconnect pst)) (free-statement* 'disconnect pst))
(handle-status 'disconnect (SQLDisconnect db*) db*) (handle-status 'disconnect (SQLDisconnect db*) db*)
@ -459,11 +461,14 @@
(call-with-lock* 'free-statement go go #f)) (call-with-lock* 'free-statement go go #f))
(define/private (free-statement* fsym pst) (define/private (free-statement* fsym pst)
(start-atomic)
(let ([stmt (send pst get-handle)]) (let ([stmt (send pst get-handle)])
(when stmt
(send pst set-handle #f) (send pst set-handle #f)
(end-atomic)
(when stmt
(handle-status 'free-statement (SQLFreeStmt stmt SQL_CLOSE) stmt) (handle-status 'free-statement (SQLFreeStmt stmt SQL_CLOSE) stmt)
(handle-status 'free-statement (SQLFreeHandle SQL_HANDLE_STMT stmt) stmt) (handle-status 'free-statement (SQLFreeHandle SQL_HANDLE_STMT stmt) stmt)
(hash-remove! statement-table pst)
(void)))) (void))))
;; Transactions ;; Transactions

View File

@ -1,6 +1,7 @@
#lang racket/base #lang racket/base
(require racket/class (require racket/class
ffi/unsafe ffi/unsafe
ffi/unsafe/atomic
"../generic/interfaces.rkt" "../generic/interfaces.rkt"
"../generic/prepared.rkt" "../generic/prepared.rkt"
"../generic/sql-data.rkt" "../generic/sql-data.rkt"
@ -18,7 +19,7 @@
busy-retry-delay) busy-retry-delay)
(define -db db) (define -db db)
(define statement-table (make-weak-hasheq)) (define statement-table (make-hasheq))
(define saved-tx-status #f) ;; set by with-lock, only valid while locked (define saved-tx-status #f) ;; set by with-lock, only valid while locked
(inherit call-with-lock* (inherit call-with-lock*
@ -149,33 +150,35 @@
pst))) pst)))
(define/public (disconnect) (define/public (disconnect)
;; FIXME: Reorder effects to be more robust if thread killed within disconnect (?)
(define (go) (define (go)
(when -db (start-atomic)
(let ([db -db] (let ([db -db])
[statements (hash-map statement-table (lambda (k v) k))])
(set! -db #f) (set! -db #f)
(set! statement-table #f) (end-atomic)
(when db
(let ([statements (hash-map statement-table (lambda (k v) k))])
(for ([pst (in-list statements)]) (for ([pst (in-list statements)])
(let ([stmt (send pst get-handle)]) (do-free-statement 'disconnect pst))
(when stmt (HANDLE 'disconnect2 (sqlite3_close db))
(send pst set-handle #f) (void)))))
(HANDLE 'disconnect (sqlite3_finalize stmt)))))
(HANDLE 'disconnect (sqlite3_close db))
(void))))
(call-with-lock* 'disconnect go go #f)) (call-with-lock* 'disconnect go go #f))
(define/public (get-base) this) (define/public (get-base) this)
(define/public (free-statement pst) (define/public (free-statement pst)
(define (go) (define (go) (do-free-statement 'free-statement pst))
(let ([stmt (send pst get-handle)])
(when stmt
(send pst set-handle #f)
(HANDLE 'free-statement (sqlite3_finalize stmt))
(void))))
(call-with-lock* 'free-statement go go #f)) (call-with-lock* 'free-statement go go #f))
(define/private (do-free-statement fsym pst)
(start-atomic)
(let ([stmt (send pst get-handle)])
(send pst set-handle #f)
(end-atomic)
(hash-remove! statement-table pst)
(when stmt
(HANDLE fsym (sqlite3_finalize stmt))
(void))))
;; == Transactions ;; == Transactions
@ -262,7 +265,7 @@
;; Can't figure out how to test... ;; Can't figure out how to test...
(define/private (handle-status who s) (define/private (handle-status who s)
(when (memv s maybe-rollback-status-list) (when (memv s maybe-rollback-status-list)
(when (and saved-tx-status (not (get-tx-status -db))) ;; was in trans, now not (when (and saved-tx-status -db (not (get-tx-status -db))) ;; was in trans, now not
(set! tx-status 'invalid))) (set! tx-status 'invalid)))
(handle-status* who s -db)) (handle-status* who s -db))

View File

@ -132,6 +132,14 @@
(_fun _sqlite3_database (_fun _sqlite3_database
-> _bool)) -> _bool))
(define-sqlite sqlite3_next_stmt
(_fun _sqlite3_database _sqlite3_statement/null
-> _sqlite3_statement/null))
(define-sqlite sqlite3_sql
(_fun _sqlite3_statement
-> _string))
;; ---------------------------------------- ;; ----------------------------------------
#| #|