use std::ffi::c_void; use std::os::raw::c_int; use std::slice; use std::sync::{Condvar, Mutex}; use libsqlite3_sys::{sqlite3, sqlite3_unlock_notify, SQLITE_OK}; use crate::SqliteError; // Wait for unlock notification (https://www.sqlite.org/unlock_notify.html) pub unsafe fn wait(conn: *mut sqlite3) -> Result<(), SqliteError> { let notify = Notify::new(); if sqlite3_unlock_notify( conn, Some(unlock_notify_cb), ¬ify as *const Notify as *mut Notify as *mut _, ) != SQLITE_OK { return Err(SqliteError::new(conn)); } notify.wait(); Ok(()) } unsafe extern "C" fn unlock_notify_cb(ptr: *mut *mut c_void, len: c_int) { let ptr = ptr as *mut &Notify; // We don't have a choice; we can't panic and unwind into FFI here. let slice = slice::from_raw_parts(ptr, usize::try_from(len).unwrap_or(0)); for notify in slice { notify.fire(); } } struct Notify { mutex: Mutex, condvar: Condvar, } impl Notify { fn new() -> Self { Self { mutex: Mutex::new(false), condvar: Condvar::new(), } } fn wait(&self) { // We only want to wait until the lock is available again. #[allow(let_underscore_lock)] let _ = self .condvar .wait_while(self.mutex.lock().unwrap(), |fired| !*fired) .unwrap(); } fn fire(&self) { let mut lock = self.mutex.lock().unwrap(); *lock = true; self.condvar.notify_one(); } }