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<Connection>>>;
12/// Keeps the wrapper alive for as long as this guard exists. `None` when the caller
13/// already holds a `&TransactionWrapper`, where borrowck provides the same guarantee.
14/// `Some` when the caller upgraded a weak reference; this holds the reference for the
15/// duration that this connection is held, ensuring that it stays alive.
16type Keepalive = Option<ArcWithReadGuard<Transaction>>;
17
18/// A guard over the connection belonging to an in-flight transaction.
19///
20/// Derefs to the [`Connection`] on which `BEGIN IMMEDIATE` was issued, so reads through it
21/// see the transaction's uncommitted writes and writes through it join the transaction.
22///
23/// Field order is load-bearing, for the same reason as [`ArcWithReadGuard`]: `guard` must be
24/// released before `_keepalive`. Were `_keepalive` to drop first it could drop the wrapper's
25/// last strong reference, and `TransactionWrapper::drop` would then block trying to lock a
26/// mutex we still hold.
27pub struct TransactionConnection {
28    guard: InternalGuard,
29    _keepalive: Keepalive,
30}
31
32impl TransactionConnection {
33    /// Construct this transaction connection, checking that the transaction is in fact still alive.
34    fn new(guard: InternalGuard, keepalive: Keepalive) -> CryptoKeystoreResult<Self> {
35        if !Transaction::is_active(guard.as_ref().expect(GUARD_EXPECTATION)) {
36            return Err(CryptoKeystoreError::UnexpectedRollback);
37        }
38        Ok(Self {
39            guard,
40            _keepalive: keepalive,
41        })
42    }
43}
44
45impl Deref for TransactionConnection {
46    type Target = Connection;
47
48    fn deref(&self) -> &Connection {
49        self.guard.as_ref().expect(GUARD_EXPECTATION)
50    }
51}
52
53impl Transaction {
54    /// Guard over this transaction's connection.
55    ///
56    /// Errors if SQLite has already rolled this transaction back under us.
57    pub fn conn(&self) -> CryptoKeystoreResult<TransactionConnection> {
58        TransactionConnection::new(self.conn.lock_arc(), None)
59    }
60}
61
62impl TransactionConnection {
63    /// As [`TransactionWrapper::conn`], for callers who reached the transaction
64    /// through a weak reference and must keep it alive.
65    pub(crate) fn shared(wrapper: ArcWithReadGuard<Transaction>) -> CryptoKeystoreResult<Self> {
66        let guard = wrapper.conn.lock_arc();
67        Self::new(guard, Some(wrapper))
68    }
69}