Skip to main content

cartel_sqlite/runtime/raw/
mod.rs

1mod vfs;
2
3use crate::{
4    catalogs, conn,
5    runtime::{self, Error, heap},
6    sys,
7};
8use o3::alloc;
9use std::{mem, ptr};
10
11#[repr(C, align(64))]
12struct Unit {
13    bytes: [mem::MaybeUninit<u8>; Span::ALIGNMENT],
14}
15
16pub(crate) struct Memory {
17    _memory: alloc::array::Block<mem::MaybeUninit<Unit>, usize>,
18}
19
20pub(crate) struct Span {
21    base: ptr::NonNull<u8>,
22    bytes: usize,
23}
24
25/// Exclusive native custody lasts for the process once allocator hooks are installed, even if installation fails; copying or dropping `Runtime` does not release it.
26///
27/// Arena-backed native handles and allocations must remain exclusively owned by their originating worker until release; raw calls and other wrappers must preserve this custody and Cartel's admitted execution. Foreign initialization and VFS allocations retain their std allocation family and audited native synchronization.
28///
29/// After allocator hook installation, callers must not reconfigure or replace the allocator, page cache, VFS or callbacks, enable URI interpretation, shared caches or native workers, install unaudited extensions, or publish arena-backed storage into global native state.
30#[must_use]
31pub struct Permit(pub(super) ());
32
33/// Exclusive installation authority for a build with infallible native shutdown.
34struct Setup(Permit);
35
36impl Permit {
37    /// # Safety
38    /// SQLite must be uninitialized, and no other thread may call SQLite until this permit is consumed by `Runtime::install` and that call returns; the caller must uphold [`Permit`]'s process-lifetime custody contract.
39    /// The initial default VFS, mutex methods, logging callbacks and auto-extension registry must belong to the audited native implementation without unaudited callbacks or extensions; allocator and page-cache tables replaced without execution may already be installed.
40    pub unsafe fn exclusive() -> Self {
41        Self(())
42    }
43
44    pub(super) fn initialize(self) -> Result<Self, Error> {
45        if unsafe {
46            libsqlite3_sys::sqlite3_compileoption_used(c"ENABLE_MEMORY_MANAGEMENT".as_ptr())
47        } != 0
48        {
49            return Err(Error::GlobalCache);
50        }
51        if unsafe {
52            libsqlite3_sys::sqlite3_compileoption_used(c"DISABLE_PAGECACHE_OVERFLOW_STATS".as_ptr())
53        } == 0
54        {
55            return Err(Error::CacheStats);
56        }
57        if unsafe { libsqlite3_sys::sqlite3_compileoption_used(c"ENABLE_STMTVTAB".as_ptr()) } != 0 {
58            return Err(Error::StatementVtab);
59        }
60        if unsafe { libsqlite3_sys::sqlite3_compileoption_used(c"OMIT_WSD".as_ptr()) } != 0 {
61            return Err(Error::WritableStatic);
62        }
63        if unsafe { libsqlite3_sys::sqlite3_compileoption_used(c"ENABLE_SQLLOG".as_ptr()) } != 0 {
64            return Err(Error::StatementLog);
65        }
66        Setup(self).initialize()
67    }
68}
69
70impl Setup {
71    fn initialize(self) -> Result<Permit, Error> {
72        use heap::raw;
73        use vfs::Vfs;
74
75        let code =
76            unsafe { libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_MULTITHREAD) };
77        if code != libsqlite3_sys::SQLITE_OK {
78            return Err(Error::Threading(sys::Code::new(code)));
79        }
80        // SAFETY: MULTITHREAD admitted the uninitialized phase, and Permit excludes
81        // concurrent calls. These native cases only copy values, invoke no callbacks,
82        // and return SQLITE_OK without initializing SQLite.
83        unsafe {
84            libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_URI, 0);
85            libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_MEMSTATUS, 0);
86            // Each connection receives its owned lookaside before its first SQL.
87            libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_LOOKASIDE, 0, 0);
88            libsqlite3_sys::sqlite3_config(
89                libsqlite3_sys::SQLITE_CONFIG_PAGECACHE,
90                ptr::null_mut::<std::ffi::c_void>(),
91                0,
92                20,
93            );
94        }
95        let cache = libsqlite3_sys::sqlite3_pcache_methods2 {
96            iVersion: 1,
97            pArg: ptr::null_mut(),
98            xInit: None,
99            xShutdown: None,
100            xCreate: None,
101            xCachesize: None,
102            xPagecount: None,
103            xFetch: None,
104            xUnpin: None,
105            xRekey: None,
106            xTruncate: None,
107            xDestroy: None,
108            xShrink: None,
109        };
110        // SAFETY: the admitted phase permits this callback-free table copy.
111        // A null xInit selects the audited native cache, replacing any prior custom cache.
112        unsafe {
113            libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_PCACHE2, &raw const cache);
114        }
115        let hooks = raw::methods();
116        // SAFETY: the same phase is retained; MALLOC copies this complete table by
117        // value and returns SQLITE_OK without invoking its callbacks.
118        unsafe {
119            libsqlite3_sys::sqlite3_config(libsqlite3_sys::SQLITE_CONFIG_MALLOC, &raw const hooks);
120        }
121        raw::foreign(|| {
122            let code = unsafe { libsqlite3_sys::sqlite3_initialize() };
123            let result = if code == libsqlite3_sys::SQLITE_OK {
124                unsafe { Vfs::install() }
125            } else {
126                Err(Error::Initialization(sys::Code::new(code)))
127            };
128            if result.is_err() {
129                // SAFETY: Setup retains exclusive startup authority and rejects OMIT_WSD,
130                // the only native build path where shutdown can return a non-OK code.
131                unsafe { libsqlite3_sys::sqlite3_shutdown() };
132            }
133            result
134        })?;
135        Ok(self.0)
136    }
137}
138
139impl Memory {
140    pub(in crate::runtime) fn new<I: catalogs::Catalog>(
141        layout: &runtime::Layout<'_, I>,
142    ) -> Result<(Self, heap::raw::Span, Span, Span, Span, Span), runtime::Error> {
143        // Layout's private constructor retains Memory::admit's exact partition proof.
144        let (sqlite, collect_bytes, statement_bytes, lookaside_bytes) = layout.dimensions();
145        let phase = conn::raw::Vacant::<'_, '_, I>::LAYOUT;
146        let prefix = phase.size().next_multiple_of(Span::ALIGNMENT);
147        let collect = prefix + heap::layout::Layout::PREFIX_BYTES + sqlite.bytes();
148        let stmts = collect + collect_bytes.next_multiple_of(Span::ALIGNMENT);
149        let aside = stmts + statement_bytes;
150        let bytes = aside + lookaside_bytes.next_multiple_of(Span::ALIGNMENT);
151        let units = bytes / Span::ALIGNMENT;
152        let mut memory =
153            alloc::array::Block::try_new(units, ()).map_err(runtime::Error::Allocation)?;
154        // SAFETY: a nonempty checked byte total produces at least one allocated Unit.
155        let base = unsafe { ptr::NonNull::new_unchecked(memory.as_mut_ptr().cast()) };
156        let start = Span::new(base, prefix, collect - prefix).base();
157        let collect = Span::new(base, collect, collect_bytes);
158        let stmts = Span::new(base, stmts, aside - stmts);
159        let aside = Span::new(base, aside, bytes - aside);
160        let phase = Span::new(base, 0, phase.size());
161        // SAFETY: start and Layout describe the checked SQLite partition in this allocation.
162        let sqlite = unsafe { heap::raw::Span::new(start, sqlite) };
163        Ok((
164            Self { _memory: memory },
165            sqlite,
166            collect,
167            stmts,
168            aside,
169            phase,
170        ))
171    }
172}
173
174impl Span {
175    pub(crate) const ALIGNMENT: usize = 64;
176
177    fn new(base: ptr::NonNull<u8>, start: usize, bytes: usize) -> Self {
178        // SAFETY: Memory bounds every partition start by its checked total allocation size.
179        let base = unsafe { ptr::NonNull::new_unchecked(base.as_ptr().add(start)) };
180        Self { base, bytes }
181    }
182
183    pub(crate) const fn into_parts(self) -> (ptr::NonNull<u8>, usize) {
184        (self.base, self.bytes)
185    }
186
187    pub(crate) const fn as_mut_ptr(&mut self) -> *mut u8 {
188        self.base.as_ptr()
189    }
190
191    pub(crate) const fn len(&self) -> usize {
192        self.bytes
193    }
194
195    pub(crate) const fn base(&self) -> ptr::NonNull<u8> {
196        self.base
197    }
198}
199
200const _: () = {
201    assert!(mem::size_of::<Unit>() == Span::ALIGNMENT);
202    assert!(mem::align_of::<Unit>() == Span::ALIGNMENT);
203};