Skip to main content

core_crypto_keystore/transaction/
conn.rs

1use std::ops::Deref;
2
3use async_lock::MutexGuardArc;
4use parking_lot::{ArcMutexGuard, RawMutex};
5use rusqlite::Connection;
6
7use super::{GUARD_EXPECTATION, Transaction};
8use crate::{CryptoKeystoreError, CryptoKeystoreResult, unique_arc::ArcWithReadGuard};
9
10/// The type of the guard we end up holding over the connection.
11type InternalGuard = ArcMutexGuard<RawMutex, Option<MutexGuardArc<crate::connection::ManagedConnection>>>;
12/// Keeps the wrapper alive for as long as this guard exists.
13///
14/// - `None` when the caller already holds a [`&Transaction`][Transaction], where borrowck provides the same guarantee.
15/// - `Some` when the caller upgraded a weak reference; this holds the reference for the duration that this connection
16///   is held, ensuring that it stays alive.
17type Keepalive = Option<ArcWithReadGuard<Transaction>>;
18
19/// A guard over the connection belonging to an in-flight transaction.
20///
21/// Derefs to the [`Connection`] on which `BEGIN IMMEDIATE` was issued, so reads through it
22/// see the transaction's uncommitted writes and writes through it join the transaction.
23///
24/// Field order is load-bearing, for the same reason as [`ArcWithReadGuard`]: `guard` must be
25/// released before `_keepalive`. Were `_keepalive` to drop first it could drop the wrapper's
26/// last strong reference, and [`Transaction::drop`] would then block trying to lock a
27/// mutex we still hold.
28pub struct TransactionConnection {
29    guard: InternalGuard,
30    _sqlite: crate::connection::SqliteGuard,
31    _keepalive: Keepalive,
32}
33
34impl TransactionConnection {
35    /// Construct this transaction connection, checking that the transaction is in fact still alive.
36    fn new(
37        guard: InternalGuard,
38        keepalive: Keepalive,
39        sqlite: crate::connection::SqliteGuard,
40    ) -> CryptoKeystoreResult<Self> {
41        let connection = Self {
42            guard,
43            _sqlite: sqlite,
44            _keepalive: keepalive,
45        };
46        // Construct first so field order also releases locks before the keepalive
47        // on the error path.
48        if !Transaction::is_active(&connection) {
49            return Err(CryptoKeystoreError::UnexpectedRollback);
50        }
51        Ok(connection)
52    }
53}
54
55impl Deref for TransactionConnection {
56    type Target = Connection;
57
58    fn deref(&self) -> &Connection {
59        self.guard.as_ref().expect(GUARD_EXPECTATION)
60    }
61}
62
63impl Transaction {
64    /// Guard over this transaction's connection.
65    ///
66    /// Errors if SQLite has already rolled this transaction back under us.
67    pub fn conn(&self) -> CryptoKeystoreResult<TransactionConnection> {
68        let sqlite = crate::connection::SqliteGuard::lock();
69        TransactionConnection::new(self.conn.lock_arc(), None, sqlite)
70    }
71}
72
73impl TransactionConnection {
74    /// As [`Transaction::conn`], for callers who reached the transaction
75    /// through a weak reference and must keep it alive.
76    pub(crate) fn shared(wrapper: ArcWithReadGuard<Transaction>) -> CryptoKeystoreResult<Self> {
77        let sqlite = crate::connection::SqliteGuard::lock();
78        let guard = wrapper.conn.lock_arc();
79        Self::new(guard, Some(wrapper), sqlite)
80    }
81}