Use std::ptr::Unique to ensure SqliteConnection is Send.

Semantically the C resource is owned by the SqliteConnection, so Unique
is correct. Being Send is safe because sqlite3 can only possibly be
thread unsafe in cases when there is simultaneous access from multiple
threads, but transferring ownership between threads doesn't allow that
to occur.
This commit is contained in:
Huon Wilson 2015-03-29 21:25:46 +11:00
parent 4b085b9103
commit 57ca5f3b51

View File

@ -48,7 +48,7 @@
//! } //! }
//! } //! }
//! ``` //! ```
#![feature(unsafe_destructor)] #![feature(unsafe_destructor, unique)]
#![cfg_attr(test, feature(test))] #![cfg_attr(test, feature(test))]
extern crate libc; extern crate libc;
@ -56,7 +56,7 @@ extern crate libsqlite3_sys as ffi;
#[macro_use] extern crate bitflags; #[macro_use] extern crate bitflags;
use std::mem; use std::mem;
use std::ptr; use std::ptr::{self, Unique};
use std::fmt; use std::fmt;
use std::path::{Path}; use std::path::{Path};
use std::error; use std::error;
@ -419,7 +419,7 @@ impl fmt::Debug for SqliteConnection {
} }
struct InnerSqliteConnection { struct InnerSqliteConnection {
db: *mut ffi::Struct_sqlite3, db: Unique<ffi::Struct_sqlite3>,
} }
bitflags! { bitflags! {
@ -463,15 +463,19 @@ impl InnerSqliteConnection {
ffi::sqlite3_close(db); ffi::sqlite3_close(db);
return Err(e); return Err(e);
} }
Ok(InnerSqliteConnection{ db: db }) Ok(InnerSqliteConnection{ db: Unique::new(db) })
} }
} }
fn db(&self) -> *mut ffi::Struct_sqlite3 {
unsafe {self.db.get() as *const _ as *mut _}
}
fn decode_result(&mut self, code: c_int) -> SqliteResult<()> { fn decode_result(&mut self, code: c_int) -> SqliteResult<()> {
if code == ffi::SQLITE_OK { if code == ffi::SQLITE_OK {
Ok(()) Ok(())
} else { } else {
Err(SqliteError::from_handle(self.db, code)) Err(SqliteError::from_handle(self.db(), code))
} }
} }
@ -486,16 +490,18 @@ impl InnerSqliteConnection {
} }
fn close(&mut self) -> SqliteResult<()> { fn close(&mut self) -> SqliteResult<()> {
let r = unsafe { ffi::sqlite3_close(self.db) }; unsafe {
self.db = ptr::null_mut(); let r = ffi::sqlite3_close(self.db());
self.decode_result(r) self.db = Unique::new(ptr::null_mut());
self.decode_result(r)
}
} }
fn execute_batch(&mut self, sql: &str) -> SqliteResult<()> { fn execute_batch(&mut self, sql: &str) -> SqliteResult<()> {
let c_sql = try!(str_to_cstring(sql)); let c_sql = try!(str_to_cstring(sql));
unsafe { unsafe {
let mut errmsg: *mut c_char = mem::uninitialized(); let mut errmsg: *mut c_char = mem::uninitialized();
let r = ffi::sqlite3_exec(self.db, c_sql.as_ptr(), None, ptr::null_mut(), &mut errmsg); let r = ffi::sqlite3_exec(self.db(), c_sql.as_ptr(), None, ptr::null_mut(), &mut errmsg);
self.decode_result_with_errmsg(r, errmsg) self.decode_result_with_errmsg(r, errmsg)
} }
} }
@ -523,7 +529,7 @@ impl InnerSqliteConnection {
fn last_insert_rowid(&self) -> i64 { fn last_insert_rowid(&self) -> i64 {
unsafe { unsafe {
ffi::sqlite3_last_insert_rowid(self.db) ffi::sqlite3_last_insert_rowid(self.db())
} }
} }
@ -534,7 +540,7 @@ impl InnerSqliteConnection {
let c_sql = try!(str_to_cstring(sql)); let c_sql = try!(str_to_cstring(sql));
let r = unsafe { let r = unsafe {
let len_with_nul = (sql.len() + 1) as c_int; let len_with_nul = (sql.len() + 1) as c_int;
ffi::sqlite3_prepare_v2(self.db, c_sql.as_ptr(), len_with_nul, &mut c_stmt, ffi::sqlite3_prepare_v2(self.db(), c_sql.as_ptr(), len_with_nul, &mut c_stmt,
ptr::null_mut()) ptr::null_mut())
}; };
self.decode_result(r).map(|_| { self.decode_result(r).map(|_| {
@ -543,7 +549,7 @@ impl InnerSqliteConnection {
} }
fn changes(&mut self) -> c_int { fn changes(&mut self) -> c_int {
unsafe{ ffi::sqlite3_changes(self.db) } unsafe{ ffi::sqlite3_changes(self.db()) }
} }
} }
@ -814,6 +820,14 @@ mod test {
extern crate libsqlite3_sys as ffi; extern crate libsqlite3_sys as ffi;
use super::*; use super::*;
// this function is never called, but is still type checked; in
// particular, calls with specific instantiations will require
// that those types are `Send`.
#[allow(dead_code, unconditional_recursion)]
fn ensure_send<T: Send>() {
ensure_send::<SqliteConnection>();
}
fn checked_memory_handle() -> SqliteConnection { fn checked_memory_handle() -> SqliteConnection {
SqliteConnection::open_in_memory().unwrap() SqliteConnection::open_in_memory().unwrap()
} }