hydrate_loader/
artifact_storage.rs

1use hydrate_base::{
2    handle::{ArtifactHandle, RefOp, TypedArtifactStorage},
3    ArtifactId, LoadHandle,
4};
5use std::{collections::HashMap, error::Error, sync::Mutex};
6
7use crate::storage::{ArtifactLoadOp, ArtifactStorage};
8use crate::ArtifactTypeId;
9use crossbeam_channel::{Receiver, Sender};
10use downcast_rs::Downcast;
11use hydrate_base::handle::LoaderInfoProvider;
12use hydrate_base::handle::SerdeContext;
13use std::marker::PhantomData;
14use type_uuid::TypeUuid;
15
16// Used to dynamic dispatch into a storage, supports checked downcasting
17pub trait DynArtifactStorage: Downcast + Send {
18    fn load_artifact(
19        &mut self,
20        loader_info: &dyn LoaderInfoProvider,
21        artifact_id: ArtifactId,
22        data: &[u8],
23        load_handle: LoadHandle,
24        load_op: ArtifactLoadOp,
25    ) -> Result<(), Box<dyn Error + Send + 'static>>;
26    fn commit_artifact(
27        &mut self,
28        handle: LoadHandle,
29    );
30    fn free_artifact(
31        &mut self,
32        handle: LoadHandle,
33    );
34
35    fn type_name(&self) -> &'static str;
36}
37
38downcast_rs::impl_downcast!(DynArtifactStorage);
39
40pub struct ArtifactStorageSetInner {
41    storage: HashMap<ArtifactTypeId, Box<dyn DynArtifactStorage>>,
42    data_to_artifact_type_uuid: HashMap<ArtifactTypeId, ArtifactTypeId>,
43    artifact_to_data_type_uuid: HashMap<ArtifactTypeId, ArtifactTypeId>,
44    refop_sender: Sender<RefOp>,
45}
46
47// Contains a storage per artifact type
48pub struct ArtifactStorageSet {
49    inner: Mutex<ArtifactStorageSetInner>,
50}
51
52impl ArtifactStorageSet {
53    pub fn new(refop_sender: Sender<RefOp>) -> Self {
54        let inner = ArtifactStorageSetInner {
55            storage: Default::default(),
56            data_to_artifact_type_uuid: Default::default(),
57            artifact_to_data_type_uuid: Default::default(),
58            refop_sender,
59        };
60
61        Self {
62            inner: Mutex::new(inner),
63        }
64    }
65
66    pub fn add_storage<T>(&self)
67    where
68        T: TypeUuid + for<'a> serde::Deserialize<'a> + 'static + Send,
69    {
70        let mut inner = self.inner.lock().unwrap();
71        let refop_sender = inner.refop_sender.clone();
72        let old = inner.data_to_artifact_type_uuid.insert(
73            ArtifactTypeId::from_bytes(T::UUID),
74            ArtifactTypeId::from_bytes(T::UUID),
75        );
76        assert!(old.is_none());
77        let old = inner.artifact_to_data_type_uuid.insert(
78            ArtifactTypeId::from_bytes(T::UUID),
79            ArtifactTypeId::from_bytes(T::UUID),
80        );
81        assert!(old.is_none());
82        inner.storage.insert(
83            ArtifactTypeId::from_bytes(T::UUID),
84            Box::new(Storage::<T>::new(
85                refop_sender,
86                Box::new(DefaultArtifactLoader::default()),
87            )),
88        );
89    }
90
91    pub fn add_storage_with_loader<ArtifactDataT, ArtifactT, LoaderT>(
92        &self,
93        loader: Box<LoaderT>,
94    ) where
95        ArtifactDataT: TypeUuid + for<'a> serde::Deserialize<'a> + 'static,
96        ArtifactT: TypeUuid + 'static + Send,
97        LoaderT: DynArtifactLoader<ArtifactT> + 'static,
98    {
99        let mut inner = self.inner.lock().unwrap();
100        let refop_sender = inner.refop_sender.clone();
101        let old = inner.data_to_artifact_type_uuid.insert(
102            ArtifactTypeId::from_bytes(ArtifactDataT::UUID),
103            ArtifactTypeId::from_bytes(ArtifactT::UUID),
104        );
105        assert!(old.is_none());
106        let old = inner.artifact_to_data_type_uuid.insert(
107            ArtifactTypeId::from_bytes(ArtifactT::UUID),
108            ArtifactTypeId::from_bytes(ArtifactDataT::UUID),
109        );
110        assert!(old.is_none());
111        inner.storage.insert(
112            ArtifactTypeId::from_bytes(ArtifactT::UUID),
113            Box::new(Storage::<ArtifactT>::new(refop_sender, loader)),
114        );
115    }
116
117    pub fn artifact_to_data_type_uuid<ArtifactT>(&self) -> Option<ArtifactTypeId>
118    where
119        ArtifactT: TypeUuid + 'static + Send,
120    {
121        let inner = self.inner.lock().unwrap();
122        inner
123            .artifact_to_data_type_uuid
124            .get(&ArtifactTypeId::from_bytes(ArtifactT::UUID))
125            .cloned()
126    }
127}
128
129// Implement distill's ArtifactStorage - an untyped trait that finds the artifact_type's storage and
130// forwards the call
131impl ArtifactStorage for ArtifactStorageSet {
132    fn load_artifact(
133        &mut self,
134        loader_info: &dyn LoaderInfoProvider,
135        artifact_type_id: &ArtifactTypeId,
136        artifact_id: ArtifactId,
137        data: Vec<u8>,
138        load_handle: LoadHandle,
139        load_op: ArtifactLoadOp,
140    ) -> Result<(), Box<dyn Error + Send + 'static>> {
141        let mut inner = self.inner.lock().unwrap();
142
143        let artifact_type_id = *inner
144            .data_to_artifact_type_uuid
145            .get(artifact_type_id)
146            .expect("unknown artifact data type");
147
148        let x = inner
149            .storage
150            .get_mut(&artifact_type_id)
151            .expect("unknown artifact type")
152            .load_artifact(loader_info, artifact_id, &data, load_handle, load_op);
153        x
154    }
155
156    fn commit_artifact(
157        &mut self,
158        artifact_data_type_id: ArtifactTypeId,
159        load_handle: LoadHandle,
160    ) {
161        let mut inner = self.inner.lock().unwrap();
162
163        let artifact_type_id = *inner
164            .data_to_artifact_type_uuid
165            .get(&artifact_data_type_id)
166            .expect("unknown artifact data type");
167
168        inner
169            .storage
170            .get_mut(&artifact_type_id)
171            .expect("unknown artifact type")
172            .commit_artifact(load_handle)
173    }
174
175    fn free_artifact(
176        &mut self,
177        artifact_data_type_id: ArtifactTypeId,
178        load_handle: LoadHandle,
179    ) {
180        let mut inner = self.inner.lock().unwrap();
181
182        let artifact_type_id = *inner
183            .data_to_artifact_type_uuid
184            .get(&artifact_data_type_id)
185            .expect("unknown artifact data type");
186
187        inner
188            .storage
189            .get_mut(&artifact_type_id)
190            .expect("unknown artifact type")
191            .free_artifact(load_handle)
192    }
193}
194
195// Implement distill's TypedArtifactStorage - a typed trait that finds the artifact_type's storage and
196// forwards the call
197impl<A: TypeUuid + 'static + Send> TypedArtifactStorage<A> for ArtifactStorageSet {
198    fn get<T: ArtifactHandle>(
199        &self,
200        handle: &T,
201    ) -> Option<&A> {
202        // This transmute can probably be unsound, but I don't have the energy to fix it right now
203        unsafe {
204            std::mem::transmute(
205                self.inner
206                    .lock()
207                    .unwrap()
208                    .storage
209                    .get(&ArtifactTypeId::from_bytes(A::UUID))
210                    .expect("unknown artifact type")
211                    .as_ref()
212                    .downcast_ref::<Storage<A>>()
213                    .expect("failed to downcast")
214                    .get(handle),
215            )
216        }
217    }
218}
219
220// Loaders can return immediately by value, or later by returning a channel
221pub enum UpdateArtifactResult<ArtifactT>
222where
223    ArtifactT: Send,
224{
225    Result(ArtifactT),
226    AsyncResult(Receiver<ArtifactT>),
227}
228
229// Implements loading logic (i.e. turning bytes into an artifact. The artifact may contain runtime-only
230// data and may be created asynchronously
231pub trait DynArtifactLoader<ArtifactT>: Send
232where
233    ArtifactT: TypeUuid + 'static + Send,
234{
235    fn load_artifact(
236        &mut self,
237        refop_sender: &Sender<RefOp>,
238        loader_info: &dyn LoaderInfoProvider,
239        data: &[u8],
240        load_handle: LoadHandle,
241        load_op: ArtifactLoadOp,
242    ) -> Result<UpdateArtifactResult<ArtifactT>, Box<dyn Error + Send + 'static>>;
243
244    fn commit_artifact(
245        &mut self,
246        handle: LoadHandle,
247    );
248
249    fn free_artifact(
250        &mut self,
251        handle: LoadHandle,
252    );
253}
254
255// A simple loader that just deserializes data
256struct DefaultArtifactLoader<ArtifactDataT>
257where
258    ArtifactDataT: TypeUuid + Send + for<'a> serde::Deserialize<'a> + 'static,
259{
260    phantom_data: PhantomData<ArtifactDataT>,
261}
262
263impl<ArtifactDataT> Default for DefaultArtifactLoader<ArtifactDataT>
264where
265    ArtifactDataT: TypeUuid + Send + for<'a> serde::Deserialize<'a> + 'static,
266{
267    fn default() -> Self {
268        DefaultArtifactLoader {
269            phantom_data: Default::default(),
270        }
271    }
272}
273
274impl<ArtifactDataT> DynArtifactLoader<ArtifactDataT> for DefaultArtifactLoader<ArtifactDataT>
275where
276    ArtifactDataT: TypeUuid + Send + for<'a> serde::Deserialize<'a> + 'static,
277{
278    fn load_artifact(
279        &mut self,
280        refop_sender: &Sender<RefOp>,
281        loader_info: &dyn LoaderInfoProvider,
282        data: &[u8],
283        _load_handle: LoadHandle,
284        load_op: ArtifactLoadOp,
285    ) -> Result<UpdateArtifactResult<ArtifactDataT>, Box<dyn Error + Send + 'static>> {
286        log::debug!("DefaultArtifactLoader load_artifact");
287
288        let artifact_data = SerdeContext::with(loader_info, refop_sender.clone(), || {
289            log::debug!("bincode deserialize");
290            let x = bincode::deserialize::<ArtifactDataT>(data)
291                // Coerce into boxed error
292                .map_err(|x| -> Box<dyn Error + Send + 'static> { Box::new(x) });
293            println!("finished deserialize");
294            x
295        })?;
296        log::debug!("call load_op.complete()");
297
298        load_op.complete();
299        log::debug!("return");
300        Ok(UpdateArtifactResult::Result(artifact_data))
301    }
302
303    fn commit_artifact(
304        &mut self,
305        _handle: LoadHandle,
306    ) {
307    }
308
309    fn free_artifact(
310        &mut self,
311        _handle: LoadHandle,
312    ) {
313    }
314}
315
316struct UncommittedArtifactState<A: Send> {
317    artifact_id: ArtifactId,
318    result: UpdateArtifactResult<A>,
319}
320
321struct ArtifactState<A> {
322    artifact_id: ArtifactId,
323    artifact: A,
324}
325
326// A strongly typed storage for a single artifact type
327pub struct Storage<ArtifactT: TypeUuid + Send> {
328    refop_sender: Sender<RefOp>,
329    artifacts: HashMap<LoadHandle, ArtifactState<ArtifactT>>,
330    uncommitted: HashMap<LoadHandle, UncommittedArtifactState<ArtifactT>>,
331    loader: Box<dyn DynArtifactLoader<ArtifactT>>,
332}
333
334impl<ArtifactT: TypeUuid + Send> Storage<ArtifactT> {
335    fn new(
336        sender: Sender<RefOp>,
337        loader: Box<dyn DynArtifactLoader<ArtifactT>>,
338    ) -> Self {
339        Self {
340            refop_sender: sender,
341            artifacts: HashMap::new(),
342            uncommitted: HashMap::new(),
343            loader,
344        }
345    }
346    fn get<T: ArtifactHandle>(
347        &self,
348        handle: &T,
349    ) -> Option<&ArtifactT> {
350        let handle = handle.direct_load_handle();
351        self.artifacts.get(&handle).map(|a| &a.artifact)
352    }
353}
354
355impl<ArtifactT: TypeUuid + 'static + Send> DynArtifactStorage for Storage<ArtifactT> {
356    fn load_artifact(
357        &mut self,
358        loader_info: &dyn LoaderInfoProvider,
359        artifact_id: ArtifactId,
360        data: &[u8],
361        load_handle: LoadHandle,
362        load_op: ArtifactLoadOp,
363    ) -> Result<(), Box<dyn Error + Send + 'static>> {
364        log::debug!(
365            "load_artifact {} {:?} {:?}",
366            core::any::type_name::<ArtifactT>(),
367            load_handle,
368            artifact_id,
369        );
370
371        let result = self.loader.load_artifact(
372            &self.refop_sender,
373            loader_info,
374            data,
375            load_handle,
376            load_op,
377        )?;
378
379        // Add to list of uncommitted artifacts
380        self.uncommitted.insert(
381            load_handle,
382            UncommittedArtifactState {
383                artifact_id,
384                result,
385            },
386        );
387
388        Ok(())
389    }
390
391    fn commit_artifact(
392        &mut self,
393        load_handle: LoadHandle,
394    ) {
395        // Remove from the uncommitted list
396        let uncommitted_artifact_state = self
397            .uncommitted
398            .remove(&load_handle)
399            .expect("artifact not present when committing");
400
401        log::debug!(
402            "commit_artifact {} {:?} {:?}",
403            core::any::type_name::<ArtifactT>(),
404            load_handle,
405            uncommitted_artifact_state.artifact_id,
406        );
407
408        let artifact_id = uncommitted_artifact_state.artifact_id;
409        let artifact = match uncommitted_artifact_state.result {
410            UpdateArtifactResult::Result(artifact) => artifact,
411            UpdateArtifactResult::AsyncResult(rx) => rx
412                .try_recv()
413                .expect("LoadOp committed but result not sent via channel"),
414        };
415
416        // If a load handler exists, trigger the commit_artifact callback
417        self.loader.commit_artifact(load_handle);
418
419        let artifact_state = ArtifactState {
420            artifact,
421            artifact_id,
422        };
423
424        // Commit the result
425        self.artifacts.insert(load_handle, artifact_state);
426    }
427
428    fn free_artifact(
429        &mut self,
430        load_handle: LoadHandle,
431    ) {
432        if let Some(artifact_state) = self.artifacts.remove(&load_handle) {
433            log::debug!(
434                "free {} {:?} {:?}",
435                core::any::type_name::<ArtifactT>(),
436                load_handle,
437                artifact_state.artifact_id
438            );
439            // Trigger the free callback on the load handler, if one exists
440            self.loader.free_artifact(load_handle);
441        }
442    }
443
444    fn type_name(&self) -> &'static str {
445        core::any::type_name::<Self>()
446    }
447}