rg3d_resource/
lib.rs

1//! Resource management
2
3#![warn(missing_docs)]
4
5use crate::core::parking_lot::{Mutex, MutexGuard};
6use crate::core::visitor::prelude::*;
7use std::{
8    borrow::Cow,
9    fmt::Debug,
10    future::Future,
11    ops::{Deref, DerefMut},
12    path::{Path, PathBuf},
13    pin::Pin,
14    sync::Arc,
15    task::{Context, Poll, Waker},
16};
17
18pub use rg3d_core as core;
19
20/// A trait for resource data.
21pub trait ResourceData: 'static + Default + Debug + Visit + Send {
22    /// Returns path of resource data.
23    fn path(&self) -> Cow<Path>;
24
25    /// Sets new path to resource data.
26    fn set_path(&mut self, path: PathBuf);
27}
28
29/// A trait for resource load error.
30pub trait ResourceLoadError: 'static + Debug + Send + Sync {}
31
32impl<T> ResourceLoadError for T where T: 'static + Debug + Send + Sync {}
33
34/// Resource could be in three possible states:
35/// 1. Pending - it is loading.
36/// 2. LoadError - an error has occurred during the load.
37/// 3. Ok - resource is fully loaded and ready to use.
38///
39/// Why it is so complex?
40/// Short answer: asynchronous loading.
41/// Long answer: when you loading a scene you expect it to be loaded as fast as
42/// possible, use all available power of the CPU. To achieve that each resource
43/// ideally should be loaded on separate core of the CPU, but since this is
44/// asynchronous, we must have the ability to track the state of the resource.
45#[derive(Debug)]
46pub enum ResourceState<T: ResourceData, E: ResourceLoadError> {
47    /// Resource is loading from external resource or in the queue to load.
48    Pending {
49        /// A path to load resource from.
50        path: PathBuf,
51        /// List of wakers to wake future when resource is fully loaded.
52        wakers: Vec<Waker>,
53    },
54    /// An error has occurred during the load.
55    LoadError {
56        /// A path at which it was impossible to load the resource.
57        path: PathBuf,
58        /// An error. This wrapped in Option only to be Default_ed.
59        error: Option<Arc<E>>,
60    },
61    /// Actual resource data when it is fully loaded.
62    Ok(T),
63}
64
65impl<T: ResourceData, E: ResourceLoadError> Visit for ResourceState<T, E> {
66    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
67        visitor.enter_region(name)?;
68
69        let mut id = self.id();
70        id.visit("Id", visitor)?;
71        if visitor.is_reading() {
72            *self = Self::from_id(id)?;
73        }
74
75        match self {
76            Self::Pending { path, .. } => panic!(
77                "Resource {} must be .await_ed before serialization",
78                path.display()
79            ),
80            // This may look strange if we attempting to save an invalid resource, but this may be
81            // actually useful - a resource may become loadable at the deserialization.
82            Self::LoadError { path, .. } => path.visit("Path", visitor)?,
83            Self::Ok(details) => details.visit("Details", visitor)?,
84        }
85
86        visitor.leave_region()
87    }
88}
89
90/// See module docs.
91#[derive(Debug, Visit)]
92pub struct Resource<T: ResourceData, E: ResourceLoadError> {
93    state: Option<Arc<Mutex<ResourceState<T, E>>>>,
94}
95
96impl<T: ResourceData, E: ResourceLoadError> PartialEq for Resource<T, E> {
97    fn eq(&self, other: &Self) -> bool {
98        match (self.state.as_ref(), other.state.as_ref()) {
99            (Some(state), Some(other_state)) => std::ptr::eq(&**state, &**other_state),
100            (None, None) => true,
101            _ => false,
102        }
103    }
104}
105
106#[doc(hidden)]
107pub struct ResourceDataRef<'a, T: ResourceData, E: ResourceLoadError> {
108    guard: MutexGuard<'a, ResourceState<T, E>>,
109}
110
111impl<'a, T: ResourceData, E: ResourceLoadError> Deref for ResourceDataRef<'a, T, E> {
112    type Target = T;
113
114    fn deref(&self) -> &Self::Target {
115        match *self.guard {
116            ResourceState::Pending { ref path, .. } => {
117                panic!(
118                    "Attempt to get reference to resource data while it is not loaded! Path is {}",
119                    path.display()
120                )
121            }
122            ResourceState::LoadError { ref path, .. } => {
123                panic!(
124                    "Attempt to get reference to resource data which failed to load! Path is {}",
125                    path.display()
126                )
127            }
128            ResourceState::Ok(ref data) => data,
129        }
130    }
131}
132
133impl<'a, T: ResourceData, E: ResourceLoadError> DerefMut for ResourceDataRef<'a, T, E> {
134    fn deref_mut(&mut self) -> &mut Self::Target {
135        match *self.guard {
136            ResourceState::Pending { ref path, .. } => {
137                panic!(
138                    "Attempt to get reference to resource data while it is not loaded! Path is {}",
139                    path.display()
140                )
141            }
142            ResourceState::LoadError { ref path, .. } => {
143                panic!(
144                    "Attempt to get reference to resource data which failed to load! Path is {}",
145                    path.display()
146                )
147            }
148            ResourceState::Ok(ref mut data) => data,
149        }
150    }
151}
152
153impl<T: ResourceData, E: ResourceLoadError> Resource<T, E> {
154    /// Creates new resource with a given state.
155    #[inline]
156    pub fn new(state: ResourceState<T, E>) -> Self {
157        Self {
158            state: Some(Arc::new(Mutex::new(state))),
159        }
160    }
161
162    /// Converts self to internal value.
163    #[inline]
164    pub fn into_inner(self) -> Arc<Mutex<ResourceState<T, E>>> {
165        self.state.unwrap()
166    }
167
168    /// Locks internal mutex provides access to the state.
169    #[inline]
170    pub fn state(&self) -> MutexGuard<'_, ResourceState<T, E>> {
171        self.state.as_ref().unwrap().lock()
172    }
173
174    /// Tries to lock internal mutex provides access to the state.
175    #[inline]
176    pub fn try_acquire_state(&self) -> Option<MutexGuard<'_, ResourceState<T, E>>> {
177        self.state.as_ref().unwrap().try_lock()
178    }
179
180    /// Returns exact amount of users of the resource.
181    #[inline]
182    pub fn use_count(&self) -> usize {
183        Arc::strong_count(self.state.as_ref().unwrap())
184    }
185
186    /// Returns a pointer as numeric value which can be used as a hash.
187    #[inline]
188    pub fn key(&self) -> usize {
189        (&**self.state.as_ref().unwrap() as *const _) as usize
190    }
191
192    /// Allows you to obtain reference to the resource data.
193    ///
194    /// # Panic
195    ///
196    /// An attempt to use method result will panic if resource is not loaded yet, or
197    /// there was load error. Usually this is ok because normally you'd chain this call
198    /// like this `resource.await?.data_ref()`. Every resource implements Future trait
199    /// and it returns Result, so if you'll await future then you'll get Result, so
200    /// call to `data_ref` will be fine.
201    #[inline]
202    pub fn data_ref(&self) -> ResourceDataRef<'_, T, E> {
203        ResourceDataRef {
204            guard: self.state(),
205        }
206    }
207}
208
209impl<T: ResourceData, E: ResourceLoadError> Default for Resource<T, E> {
210    #[inline]
211    fn default() -> Self {
212        Self { state: None }
213    }
214}
215
216impl<T: ResourceData, E: ResourceLoadError> Clone for Resource<T, E> {
217    #[inline]
218    fn clone(&self) -> Self {
219        Self {
220            state: self.state.clone(),
221        }
222    }
223}
224
225impl<T: ResourceData, E: ResourceLoadError> From<Arc<Mutex<ResourceState<T, E>>>>
226    for Resource<T, E>
227{
228    #[inline]
229    fn from(state: Arc<Mutex<ResourceState<T, E>>>) -> Self {
230        Self { state: Some(state) }
231    }
232}
233
234#[allow(clippy::from_over_into)]
235impl<T: ResourceData, E: ResourceLoadError> Into<Arc<Mutex<ResourceState<T, E>>>>
236    for Resource<T, E>
237{
238    #[inline]
239    fn into(self) -> Arc<Mutex<ResourceState<T, E>>> {
240        self.state.unwrap()
241    }
242}
243
244impl<T: ResourceData, E: ResourceLoadError> Future for Resource<T, E> {
245    type Output = Result<Self, Option<Arc<E>>>;
246
247    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
248        let state = self.as_ref().state.clone();
249        match *state.unwrap().lock() {
250            ResourceState::Pending { ref mut wakers, .. } => {
251                // Collect wakers, so we'll be able to wake task when worker thread finish loading.
252                let cx_waker = cx.waker();
253                if let Some(pos) = wakers.iter().position(|waker| waker.will_wake(cx_waker)) {
254                    wakers[pos] = cx_waker.clone();
255                } else {
256                    wakers.push(cx_waker.clone())
257                }
258
259                Poll::Pending
260            }
261            ResourceState::LoadError { ref error, .. } => Poll::Ready(Err(error.clone())),
262            ResourceState::Ok(_) => Poll::Ready(Ok(self.clone())),
263        }
264    }
265}
266
267impl<T: ResourceData, E: ResourceLoadError> ResourceState<T, E> {
268    /// Creates new resource in pending state.
269    #[inline]
270    pub fn new_pending(path: PathBuf) -> Self {
271        Self::Pending {
272            path,
273            wakers: Default::default(),
274        }
275    }
276
277    #[inline]
278    fn id(&self) -> u32 {
279        match self {
280            Self::Pending { .. } => 0,
281            Self::LoadError { .. } => 1,
282            Self::Ok(_) => 2,
283        }
284    }
285
286    #[inline]
287    fn from_id(id: u32) -> Result<Self, String> {
288        match id {
289            0 => Ok(Self::Pending {
290                path: Default::default(),
291                wakers: Default::default(),
292            }),
293            1 => Ok(Self::LoadError {
294                path: Default::default(),
295                error: None,
296            }),
297            2 => Ok(Self::Ok(Default::default())),
298            _ => Err(format!("Invalid resource id {}", id)),
299        }
300    }
301
302    /// Returns a path to the resource source.
303    #[inline]
304    pub fn path(&self) -> Cow<Path> {
305        match self {
306            Self::Pending { path, .. } => Cow::Borrowed(path.as_path()),
307            Self::LoadError { path, .. } => Cow::Borrowed(path.as_path()),
308            Self::Ok(details) => details.path(),
309        }
310    }
311
312    /// Changes ResourceState::Pending state to ResourceState::Ok(data) with given `data`.
313    /// Additionally it wakes all futures.
314    #[inline]
315    pub fn commit(&mut self, state: ResourceState<T, E>) {
316        let wakers = if let ResourceState::Pending { ref mut wakers, .. } = self {
317            std::mem::take(wakers)
318        } else {
319            unreachable!()
320        };
321
322        *self = state;
323
324        for waker in wakers {
325            waker.wake();
326        }
327    }
328}
329
330impl<T: ResourceData, E: ResourceLoadError> Default for ResourceState<T, E> {
331    fn default() -> Self {
332        Self::Ok(Default::default())
333    }
334}
335
336/// Defines a new resource type via new-type wrapper.
337#[macro_export]
338macro_rules! define_new_resource {
339    ($(#[$meta:meta])* $name:ident<$state:ty, $error:ty>) => {
340        $(#[$meta])*
341        #[derive(Clone, Debug, Default, PartialEq)]
342        #[repr(transparent)]
343        pub struct $name(pub Resource<$state, $error>);
344
345        impl Visit for $name {
346            fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
347                self.0.visit(name, visitor)
348            }
349        }
350
351        impl std::ops::Deref for $name {
352            type Target = Resource<$state, $error>;
353
354            fn deref(&self) -> &Self::Target {
355                &self.0
356            }
357        }
358
359        impl std::ops::DerefMut for $name {
360            fn deref_mut(&mut self) -> &mut Self::Target {
361                &mut self.0
362            }
363        }
364
365        impl std::future::Future for $name {
366            type Output = Result<Self, Option<std::sync::Arc<$error>>>;
367
368            fn poll(
369                mut self: std::pin::Pin<&mut Self>,
370                cx: &mut std::task::Context<'_>,
371            ) -> std::task::Poll<Self::Output> {
372                std::pin::Pin::new(&mut self.0)
373                    .poll(cx)
374                    .map(|r| r.map(|_| self.clone()))
375            }
376        }
377    };
378}