vortex_session/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4pub mod registry;
5
6use std::any::{Any, TypeId, type_name};
7use std::fmt::Debug;
8use std::hash::{BuildHasherDefault, Hasher};
9use std::ops::{Deref, DerefMut};
10use std::sync::Arc;
11
12use dashmap::{DashMap, Entry};
13use vortex_error::{VortexExpect, vortex_panic};
14
15/// A Vortex session encapsulates the set of extensible arrays, layouts, compute functions, dtypes,
16/// etc. that are available for use in a given context.
17///
18/// It is also the entry-point passed to dynamic libraries to initialize Vortex plugins.
19#[derive(Clone, Debug)]
20pub struct VortexSession(Arc<SessionVars>);
21
22impl VortexSession {
23    /// Create a new [`VortexSession`] with no session state.
24    ///
25    /// It is recommended to use the `default()` method instead provided by the main `vortex` crate.
26    pub fn empty() -> Self {
27        Self(Default::default())
28    }
29
30    /// Inserts a new session variable of type `V` with its default value.
31    ///
32    /// # Panics
33    ///
34    /// If a variable of that type already exists.
35    pub fn with<V: SessionVar + Default>(self) -> Self {
36        match self.0.entry(TypeId::of::<V>()) {
37            Entry::Occupied(_) => {
38                vortex_panic!(
39                    "Session variable of type {} already exists",
40                    type_name::<V>()
41                );
42            }
43            Entry::Vacant(e) => {
44                e.insert(Box::new(V::default()));
45            }
46        }
47        self
48    }
49}
50
51/// Trait for accessing and modifying the state of a Vortex session.
52pub trait SessionExt: Sized + private::Sealed {
53    /// Returns the [`VortexSession`].
54    fn session(&self) -> VortexSession;
55
56    /// Returns the scope variable of type `V`, or inserts a default one if it does not exist.
57    fn get<V: SessionVar>(&self) -> Ref<'_, V>;
58
59    /// Returns the scope variable of type `V`, or inserts a default one if it does not exist.
60    ///
61    /// Note that the returned value internally holds a lock on the variable.
62    fn get_mut<V: SessionVar>(&self) -> RefMut<'_, V>;
63}
64
65mod private {
66    pub trait Sealed {}
67    impl Sealed for super::VortexSession {}
68}
69
70impl SessionExt for VortexSession {
71    fn session(&self) -> VortexSession {
72        self.clone()
73    }
74
75    /// Returns the scope variable of type `V`, or inserts a default one if it does not exist.
76    fn get<V: SessionVar>(&self) -> Ref<'_, V> {
77        Ref(self
78            .0
79            .get(&TypeId::of::<V>())
80            .unwrap_or_else(|| {
81                vortex_panic!("Session has not been initialized with {}", type_name::<V>())
82            })
83            .map(|v| {
84                (**v)
85                    .as_any()
86                    .downcast_ref::<V>()
87                    .vortex_expect("Type mismatch - this is a bug")
88            }))
89    }
90
91    /// Returns the scope variable of type `V`, or inserts a default one if it does not exist.
92    ///
93    /// Note that the returned value internally holds a lock on the variable.
94    fn get_mut<V: SessionVar>(&self) -> RefMut<'_, V> {
95        RefMut(
96            self.0
97                .get_mut(&TypeId::of::<V>())
98                .unwrap_or_else(|| {
99                    vortex_panic!("Session has not been initialized with {}", type_name::<V>())
100                })
101                .map(|v| {
102                    (**v)
103                        .as_any_mut()
104                        .downcast_mut::<V>()
105                        .vortex_expect("Type mismatch - this is a bug")
106                }),
107        )
108    }
109}
110
111/// A TypeMap based on `https://docs.rs/http/1.2.0/src/http/extensions.rs.html#41-266`.
112type SessionVars = DashMap<TypeId, Box<dyn SessionVar>, BuildHasherDefault<IdHasher>>;
113
114/// With TypeIds as keys, there's no need to hash them. They are already hashes
115/// themselves, coming from the compiler. The IdHasher just holds the u64 of
116/// the TypeId, and then returns it, instead of doing any bit fiddling.
117#[derive(Default)]
118struct IdHasher(u64);
119
120impl Hasher for IdHasher {
121    #[inline]
122    fn finish(&self) -> u64 {
123        self.0
124    }
125
126    fn write(&mut self, _: &[u8]) {
127        unreachable!("TypeId calls write_u64");
128    }
129
130    #[inline]
131    fn write_u64(&mut self, id: u64) {
132        self.0 = id;
133    }
134}
135
136/// This trait defines variables that can be stored against a Vortex session.
137pub trait SessionVar: Any + Send + Sync + Debug + 'static {
138    fn as_any(&self) -> &dyn Any;
139    fn as_any_mut(&mut self) -> &mut dyn Any;
140}
141
142impl<T: Send + Sync + Debug + 'static> SessionVar for T {
143    fn as_any(&self) -> &dyn Any {
144        self
145    }
146
147    fn as_any_mut(&mut self) -> &mut dyn Any {
148        self
149    }
150}
151
152// NOTE(ngates): we don't want to expose that the internals of a session is a DashMap, so we have
153// our own wrapped Ref type.
154pub struct Ref<'a, T>(dashmap::mapref::one::MappedRef<'a, TypeId, Box<dyn SessionVar>, T>);
155impl<'a, T> Deref for Ref<'a, T> {
156    type Target = T;
157
158    fn deref(&self) -> &Self::Target {
159        &self.0
160    }
161}
162impl<'a, T> Ref<'a, T> {
163    /// Map this reference to a different target.
164    pub fn map<F, U>(self, f: F) -> Ref<'a, U>
165    where
166        F: FnOnce(&T) -> &U,
167    {
168        Ref(self.0.map(f))
169    }
170}
171
172pub struct RefMut<'a, T>(dashmap::mapref::one::MappedRefMut<'a, TypeId, Box<dyn SessionVar>, T>);
173impl<'a, T> Deref for RefMut<'a, T> {
174    type Target = T;
175
176    fn deref(&self) -> &Self::Target {
177        &self.0
178    }
179}
180impl<'a, T> DerefMut for RefMut<'a, T> {
181    fn deref_mut(&mut self) -> &mut Self::Target {
182        self.0.deref_mut()
183    }
184}
185impl<'a, T> RefMut<'a, T> {
186    /// Map this mutable reference to a different target.
187    pub fn map<F, U>(self, f: F) -> RefMut<'a, U>
188    where
189        F: FnOnce(&mut T) -> &mut U,
190    {
191        RefMut(self.0.map(f))
192    }
193}