Skip to main content

cartel_sqlite/runtime/
local.rs

1use crate::{
2    catalogs, config, conn, database, retry,
3    runtime::{self, heap, release, slot},
4};
5use dope_core::driver::storage;
6use heap::layout::Layout;
7use std::{cell, marker, mem, pin, ptr};
8
9/// Pinned SQLite resources owned by one generative driver domain.
10pub struct State<'d, I: catalogs::Catalog> {
11    retry: retry::raw::Configured<'d>,
12    slot: cell::UnsafeCell<slot::Local<I>>,
13    phase: runtime::raw::Span,
14    _retire: Retire,
15    proof: marker::PhantomData<(cell::Cell<&'d I>, marker::PhantomPinned)>,
16    value_bytes: i32,
17    statement_sql_bytes: i32,
18    database: cell::UnsafeCell<Option<database::Target<'d>>>,
19}
20
21pub(super) struct Settings {
22    pub(super) config: config::Config,
23    pub(super) value_bytes: i32,
24    pub(super) statement_sql_bytes: i32,
25}
26
27/// Drops the arena proof before reclaiming its backing allocation.
28struct Resources {
29    heap: heap::raw::Retained,
30    memory: runtime::raw::Memory,
31}
32
33struct Retire(ptr::NonNull<Resources>);
34
35impl<'d, I: catalogs::Catalog> State<'d, I> {
36    pub(crate) const RETENTION_BYTES: usize = mem::size_of::<Resources>().div_ceil(64) * 64;
37
38    pub(in crate::runtime) fn new(
39        context: &mut storage::Context<'_, 'd>,
40        settings: Settings,
41        sqlite: Layout,
42        database: database::Target<'d>,
43        stmts: usize,
44        aside: crate::memory::Lookaside,
45    ) -> Result<Self, runtime::Error> {
46        let Settings {
47            config,
48            value_bytes: values,
49            statement_sql_bytes: sql,
50        } = settings;
51        let (memory, sqlite, collect, stmts, storage, phase) = runtime::raw::Memory::new(
52            sqlite,
53            config.result_bytes(),
54            stmts,
55            aside.bytes(),
56            conn::raw::Vacant::<'_, 'd, I>::LAYOUT,
57        )?;
58        let retry = retry::raw::Configured::new(config, || {
59            context
60                .timer()
61                .try_alarm()
62                .map_err(runtime::Error::TimerCapacity)
63        })?;
64        let (slot, heap) = slot::Local::new(sqlite, collect, stmts, storage, aside)?;
65        Ok(Self {
66            retry,
67            slot: cell::UnsafeCell::new(slot),
68            phase,
69            _retire: Resources::place(memory, heap),
70            proof: marker::PhantomData,
71            value_bytes: values,
72            statement_sql_bytes: sql,
73            database: cell::UnsafeCell::new(Some(database)),
74        })
75    }
76
77    pub fn config(self: pin::Pin<&Self>) -> &config::Config {
78        self.get_ref().retry.config()
79    }
80
81    pub(crate) const fn sql_bytes(&self) -> i32 {
82        self.statement_sql_bytes
83    }
84
85    pub(crate) const fn value_bytes(&self) -> i32 {
86        self.value_bytes
87    }
88
89    pub(crate) fn try_lease<'local>(
90        self: pin::Pin<&'local Self>,
91        report: &'local mut crate::open::Completion,
92    ) -> Result<
93        (
94            database::Target<'d>,
95            slot::Vacant<'local, 'd, I>,
96            conn::raw::Vacant<'local, 'd, I>,
97        ),
98        crate::open::Error,
99    >
100    where
101        'd: 'local,
102    {
103        let report = report.admit()?;
104        let local = self.get_ref();
105        // SAFETY: this thread-bound take invokes no callbacks and retains no field borrow.
106        let database = unsafe { &mut *local.database.get() }
107            .take()
108            .ok_or(crate::open::Error::AlreadyStarted)?;
109        // SAFETY: the target is consumed once and never restored, so it grants the
110        // sole retry loan. Config stays outside every mutable slot projection.
111        let retry = unsafe { local.retry.loan() };
112        // SAFETY: the same consumed target grants the only mutable local projection.
113        // Pin preserves both owners' addresses through their returned lease.
114        let slot = unsafe { &mut *local.slot.get() };
115        let lease = slot.lease(retry, report)?;
116        // SAFETY: Memory reserved this exact catalog's phase layout. The consumed
117        // target grants its only initialization, and local retains the backing.
118        let phase = unsafe { conn::raw::Vacant::new(&local.phase) };
119        Ok((database, lease, phase))
120    }
121}
122
123impl Resources {
124    fn place(mut memory: runtime::raw::Memory, heap: heap::raw::Retained) -> Retire {
125        let pointer = memory.base().cast::<Self>();
126        // SAFETY: Memory reserves this aligned prefix for its unique retirement owner.
127        unsafe { pointer.as_ptr().write(Self { heap, memory }) };
128        Retire(pointer)
129    }
130}
131
132impl Drop for Retire {
133    fn drop(&mut self) {
134        // SAFETY: this unique non-Copy owner moves its initialized prefix once in Drop.
135        let Resources { heap, memory } = unsafe { self.0.as_ptr().read() };
136        heap.arm(release::Release::new(memory));
137    }
138}
139
140const _: () = {
141    assert!(mem::align_of::<Resources>() <= 64);
142    let fields = mem::size_of::<retry::raw::Configured<'_>>()
143        + mem::size_of::<cell::UnsafeCell<slot::Local<catalogs::Empty>>>()
144        + mem::size_of::<runtime::raw::Span>()
145        + mem::size_of::<Retire>()
146        + mem::size_of::<marker::PhantomData<(cell::Cell<&catalogs::Empty>, marker::PhantomPinned)>>(
147        )
148        + 2 * mem::size_of::<i32>()
149        + mem::size_of::<cell::UnsafeCell<Option<database::Target<'_>>>>();
150    let align = mem::align_of::<State<'_, catalogs::Empty>>();
151    assert!(mem::size_of::<State<'_, catalogs::Empty>>() == fields.next_multiple_of(align));
152};