In process locking implemented, not sqlite locking.
This commit is contained in:
@@ -56,5 +56,8 @@
|
|||||||
|
|
||||||
(define/public (key-values)
|
(define/public (key-values)
|
||||||
(ks-key-values ksh))
|
(ks-key-values ksh))
|
||||||
|
|
||||||
|
(define/public (with-lock proc)
|
||||||
|
(ks-with-lock ksh proc))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+25
-2
@@ -27,8 +27,10 @@
|
|||||||
ks-abort-transaction
|
ks-abort-transaction
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(define (any? a) #t)
|
||||||
|
|
||||||
(define-struct keystore
|
(define-struct keystore
|
||||||
(file path dbh)
|
(file path dbh lock-sem (in-lock #:mutable) (entered #:mutable))
|
||||||
#:transparent
|
#:transparent
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -46,7 +48,7 @@
|
|||||||
(let ((dbh (sqlite3-connect #:database path #:mode 'create)))
|
(let ((dbh (sqlite3-connect #:database path #:mode 'create)))
|
||||||
(query-exec dbh "CREATE TABLE IF NOT EXISTS keystore(key varchar primary key, value varchar, str_key varchar);")
|
(query-exec dbh "CREATE TABLE IF NOT EXISTS keystore(key varchar primary key, value varchar, str_key varchar);")
|
||||||
(query-exec dbh "CREATE INDEX IF NOT EXISTS keystore_idx on keystore(str_key);")
|
(query-exec dbh "CREATE INDEX IF NOT EXISTS keystore_idx on keystore(str_key);")
|
||||||
(make-keystore file path dbh))))
|
(make-keystore file path dbh (make-semaphore 1) #f 0))))
|
||||||
|
|
||||||
(define/contract (ks-close ksh)
|
(define/contract (ks-close ksh)
|
||||||
(-> keystore? boolean?)
|
(-> keystore? boolean?)
|
||||||
@@ -54,6 +56,27 @@
|
|||||||
(disconnect dbh)
|
(disconnect dbh)
|
||||||
#t))
|
#t))
|
||||||
|
|
||||||
|
(define/contract (ks-with-lock ksh proc)
|
||||||
|
(-> keystore? procedure? any?)
|
||||||
|
(dynamic-wind
|
||||||
|
(λ ()
|
||||||
|
(cond
|
||||||
|
((eq? (keystore-in-lock ksh) (current-thread))
|
||||||
|
(set-keystore-entered! ksh (+ keystore-entered ksh) 1))
|
||||||
|
(else
|
||||||
|
(semaphore-wait (keystore-lock-sem ksh))
|
||||||
|
(set-keystore-entered! ksh 1)
|
||||||
|
))
|
||||||
|
(set-keystore-in-lock! ksh (current-thread)))
|
||||||
|
proc
|
||||||
|
(λ ()
|
||||||
|
(set-keystore-entered! (- (keystore-entered ksh) 1))
|
||||||
|
(when (= (keystore-entered ksh) 0)
|
||||||
|
(set-keystore-in-lock! ksh #f)
|
||||||
|
(semaphore-post (keystore-lock-sem ksh))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
(define/contract (ks-set! ksh key value)
|
(define/contract (ks-set! ksh key value)
|
||||||
(-> keystore? any/c any/c boolean?)
|
(-> keystore? any/c any/c boolean?)
|
||||||
(ks-set!* (keystore-dbh ksh) (key->string key) (format "~a" key) (value->string value)))
|
(ks-set!* (keystore-dbh ksh) (key->string key) (format "~a" key) (value->string value)))
|
||||||
|
|||||||
@@ -83,6 +83,18 @@ Returns all keys.
|
|||||||
|
|
||||||
Returns all key-value pairs.
|
Returns all key-value pairs.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@defmethod[(with-lock [proc (-> any/c)]) any/c]{
|
||||||
|
|
||||||
|
Calls @racket[proc] while holding the lock belonging to this keystore
|
||||||
|
object and returns the value produced by @racket[proc]. Other threads
|
||||||
|
using @method[keystore% with-lock] on the same object wait until the lock is
|
||||||
|
released.
|
||||||
|
|
||||||
|
The lock is reentrant for nested calls on the same thread and is
|
||||||
|
released when control leaves @racket[proc], including when an exception
|
||||||
|
is raised. Keystore methods do not acquire this lock automatically.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@section{Examples}
|
@section{Examples}
|
||||||
@@ -116,3 +128,12 @@ Returns all key-value pairs.
|
|||||||
(send ks glob "*b*")
|
(send ks glob "*b*")
|
||||||
(send ks glob-kv "*b*")
|
(send ks glob-kv "*b*")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@subsection{Locking}
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(send ks with-lock
|
||||||
|
(λ ()
|
||||||
|
(send ks set! 'counter
|
||||||
|
(add1 (send ks get 'counter 0)))))
|
||||||
|
]
|
||||||
|
|||||||
+37
-2
@@ -19,11 +19,18 @@ stringified lowercase form, which allows glob-style queries.
|
|||||||
@defstruct*[keystore
|
@defstruct*[keystore
|
||||||
([file any/c]
|
([file any/c]
|
||||||
[path path?]
|
[path path?]
|
||||||
[dbh any/c])]{
|
[dbh any/c]
|
||||||
|
[lock-sem semaphore?]
|
||||||
|
[in-lock (or/c thread? #f)]
|
||||||
|
[entered exact-nonnegative-integer?])]{
|
||||||
|
|
||||||
Represents an open keystore. The @racket[file] field contains the
|
Represents an open keystore. The @racket[file] field contains the
|
||||||
original argument, @racket[path] is the resolved database path, and
|
original argument, @racket[path] is the resolved database path, and
|
||||||
@racket[dbh] is the SQLite connection.
|
@racket[dbh] is the SQLite connection. The remaining fields implement
|
||||||
|
the reentrant lock used by @racket[ks-with-lock]: @racket[lock-sem]
|
||||||
|
serializes access, @racket[in-lock] records the thread that holds the
|
||||||
|
lock, and @racket[entered] records that thread's nesting depth. The
|
||||||
|
@racket[in-lock] and @racket[entered] fields are mutable internal state.
|
||||||
|
|
||||||
The @tt{keystore?} predicate is altered to also check if the
|
The @tt{keystore?} predicate is altered to also check if the
|
||||||
database connection is (still) valid.
|
database connection is (still) valid.
|
||||||
@@ -151,6 +158,24 @@ Commits a transaction with "COMMIT".
|
|||||||
Aborts a transaction with "ROLLBACK".
|
Aborts a transaction with "ROLLBACK".
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@section{Locking}
|
||||||
|
|
||||||
|
@defproc[(ks-with-lock [ks keystore?] [proc (-> any/c)]) any/c]{
|
||||||
|
|
||||||
|
Calls @racket[proc] while holding the lock belonging to @racket[ks] and
|
||||||
|
returns the value produced by @racket[proc]. Other threads using
|
||||||
|
@racket[ks-with-lock] with the same keystore handle wait until the lock
|
||||||
|
is released.
|
||||||
|
|
||||||
|
The lock is reentrant: @racket[proc] may call @racket[ks-with-lock]
|
||||||
|
again on the same handle and thread. The lock is released when control
|
||||||
|
leaves @racket[proc], including when an exception is raised.
|
||||||
|
|
||||||
|
The lock coordinates only code that uses the same keystore handle and
|
||||||
|
explicitly calls @racket[ks-with-lock]. Individual keystore operations
|
||||||
|
do not acquire it automatically.
|
||||||
|
}
|
||||||
|
|
||||||
@section{Examples}
|
@section{Examples}
|
||||||
|
|
||||||
@subsection{Basic Usage}
|
@subsection{Basic Usage}
|
||||||
@@ -181,3 +206,13 @@ Aborts a transaction with "ROLLBACK".
|
|||||||
@racketblock[
|
@racketblock[
|
||||||
(ks-keys-glob ks "*b*")
|
(ks-keys-glob ks "*b*")
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@subsection{Locking Example}
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(ks-with-lock
|
||||||
|
ks
|
||||||
|
(λ ()
|
||||||
|
(ks-set! ks 'counter
|
||||||
|
(add1 (ks-get ks 'counter 0)))))
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user