Skip to content

Commit

Permalink
Make statement handle storage threadsafe
Browse files Browse the repository at this point in the history
Connection objects retain a map of ongoing statements. With SQLite,
one can use a single database handle to execute queries from many
threads when it is in serialized mode (which is the default). This
works out OK with SQLite itself, however the handling of the map of
active statement handles was not threadsafe, and this could cause
segfaults.

This refactors how active statement handles are tracked to allow the
insertion of locking, and then adds that locking.
  • Loading branch information
jnthn committed Sep 18, 2018
1 parent 11e890a commit 6385e3e
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 8 deletions.
29 changes: 25 additions & 4 deletions lib/DBDish/Connection.pm6
Expand Up @@ -17,12 +17,16 @@ unit role DBDish::Connection does DBDish::ErrorHandling;
=head5 do
=end pod

has %.Statements;
has %!statements;
has Lock $!statements-lock .= new;
has $.last-sth-id is rw;
has $.last-rows is rw;

method dispose() {
$_.dispose for %!Statements.values;
$!statements-lock.protect: {
$_.dispose for %!statements.values;
%!statements = ();
}
self._disconnect;
?($.parent.Connections{self.WHICH}:delete);
}
Expand All @@ -47,7 +51,7 @@ method prepare(Str $statement, *%args) { ... }

method do(Str $statement, *@params, *%args) {
LEAVE {
with %!Statements{$!last-sth-id} {
with $!statements-lock.protect({ %!statements{$!last-sth-id} }) {
warn "'do' should not be used for statements that return rows"
unless .Finished;
.dispose;
Expand All @@ -65,14 +69,31 @@ method do(Str $statement, *@params, *%args) {

method rows {
if $!last-sth-id {
with %!Statements{$!last-sth-id} {
with $!statements-lock.protect({ %!statements{$!last-sth-id} }) {
.rows;
} else {
$!last-rows
}
}
}

method register-statement-handle($handle) {
$!statements-lock.protect: {
%!statements{$handle.WHICH} = $handle;
}
}

method unregister-statement-handle($handle) {
$!statements-lock.protect: {
%!statements{$handle.WHICH}:delete;
}
}

method Statements() {
# Defensive copy, since %!statements access must be done under lock
$!statements-lock.protect: { %!statements.clone }
}

=begin pod
=head5 quote-identifier
Expand Down
4 changes: 2 additions & 2 deletions lib/DBDish/StatementHandle.pm6
Expand Up @@ -52,13 +52,13 @@ method !done-execute(Int $rows, $fields) {

method new(*%args) {
my \sth = ::?CLASS.bless(|%args);
%args<parent>.Statements{sth.WHICH} = sth;
%args<parent>.register-statement-handle(sth)
}

method dispose() {
self.finish unless $!Finished;
self._free;
with $.parent.Statements{self.WHICH}:delete {
with $.parent.unregister-statement-handle(self) {
$.parent.last-rows = self.rows;
True;
} else { False };
Expand Down
4 changes: 2 additions & 2 deletions lib/DBDish/mysql/Connection.pm6
Expand Up @@ -39,8 +39,8 @@ method execute(Str $statement, *%args) {
}

method insert-id() {
with $!last-sth-id andthen %!Statements{$_} {
.insert-id;
with $!last-sth-id andthen $!statements-lock.protect({ %!statements{$_} }) {
.insert-id;
}
}

Expand Down

0 comments on commit 6385e3e

Please sign in to comment.