xref: /wasmtime-44.0.1/crates/core/src/error/oom.rs (revision bc4582c3)
1 use crate::error::{Error, OomOrDynError};
2 use core::{fmt, mem, ptr::NonNull};
3 
4 /// Out-of-memory error.
5 ///
6 /// This error is the sentinel for allocation failure due to memory exhaustion.
7 ///
8 /// Constructing an [`Error`] from an `OutOfMemory` does not
9 /// allocate.
10 ///
11 /// Allocation failure inside any `Error` method that must allocate
12 /// (e.g. [`Error::context`]) will propagate an `OutOfMemory` error.
13 #[derive(Clone, Copy)]
14 // NB: `OutOfMemory`'s representation must be the same as `OomOrDynError`
15 // (and therefore also `Error`).
16 #[repr(transparent)]
17 pub struct OutOfMemory {
18     inner: NonNull<u8>,
19 }
20 
21 // Safety: The `inner` pointer is not a real pointer, it is just bitpacked size
22 // data.
23 unsafe impl Send for OutOfMemory {}
24 
25 // Safety: The `inner` pointer is not a real pointer, it is just bitpacked size
26 // data.
27 unsafe impl Sync for OutOfMemory {}
28 
29 impl fmt::Debug for OutOfMemory {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result30     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31         f.debug_struct("OutOfMemory")
32             .field(
33                 "requested_allocation_size",
34                 &self.requested_allocation_size(),
35             )
36             .finish()
37     }
38 }
39 
40 impl fmt::Display for OutOfMemory {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result41     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42         write!(
43             f,
44             "out of memory (failed to allocate {} bytes)",
45             self.requested_allocation_size()
46         )
47     }
48 }
49 
50 impl core::error::Error for OutOfMemory {
51     #[inline]
source(&self) -> Option<&(dyn core::error::Error + 'static)>52     fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
53         None
54     }
55 }
56 
57 impl OutOfMemory {
58     // NB: `OutOfMemory`'s representation must be the same as `OomOrDynError`
59     // (and therefore also `Error`).
60     const _SAME_SIZE_AS_OOM_OR_DYN_ERROR: () =
61         assert!(mem::size_of::<OutOfMemory>() == mem::size_of::<OomOrDynError>());
62     const _SAME_ALIGN_AS_OOM_OR_DYN_ERROR: () =
63         assert!(mem::align_of::<OutOfMemory>() == mem::align_of::<OomOrDynError>());
64     const _SAME_SIZE_AS_ERROR: () =
65         assert!(mem::size_of::<OutOfMemory>() == mem::size_of::<Error>());
66     const _SAME_ALIGN_AS_ERROR: () =
67         assert!(mem::align_of::<OutOfMemory>() == mem::align_of::<Error>());
68 
69     /// Construct a new `OutOfMemory` error.
70     ///
71     /// The `requested_allocation_size` argument should be the size (in bytes)
72     /// of the associated allocation that was attempted and failed.
73     ///
74     /// This operation does not allocate.
75     ///
76     /// # Example
77     ///
78     /// ```rust
79     /// # use wasmtime_internal_core::error::OutOfMemory;
80     /// # extern crate alloc;
81     /// use alloc::alloc::{Layout, alloc};
82     /// use core::ptr::NonNull;
83     ///
84     /// /// Attempt to allocate a block of memory from the global allocator,
85     /// /// returning an `OutOfMemory` error on failure.
86     /// fn try_global_alloc(layout: Layout) -> Result<NonNull<u8>, OutOfMemory> {
87     ///     if layout.size() == 0 {
88     ///         return Ok(NonNull::dangling());
89     ///     }
90     ///
91     ///     // Safety: the layout's size is non-zero.
92     ///     let ptr = unsafe { alloc(layout) };
93     ///
94     ///     if let Some(ptr) = NonNull::new(ptr) {
95     ///         Ok(ptr)
96     ///     } else {
97     ///         // The allocation failed, so return an `OutOfMemory` error,
98     ///         // passing the attempted allocation's size into the `OutOfMemory`
99     ///         // constructor.
100     ///         Err(OutOfMemory::new(layout.size()))
101     ///     }
102     /// }
103     /// ```
104     #[inline]
new(requested_allocation_size: usize) -> Self105     pub const fn new(requested_allocation_size: usize) -> Self {
106         Self {
107             inner: OomOrDynError::new_oom_ptr(requested_allocation_size),
108         }
109     }
110 
111     /// Get the size (in bytes) of the associated allocation that was attempted
112     /// and which failed.
113     ///
114     /// Very large allocation sizes (near `isize::MAX` and larger) may be capped
115     /// to a maximum value.
116     ///
117     /// # Example
118     ///
119     /// ```rust
120     /// # use wasmtime_internal_core::error::OutOfMemory;
121     /// let oom = OutOfMemory::new(8192);
122     /// assert_eq!(oom.requested_allocation_size(), 8192);
123     /// ```
124     #[inline]
requested_allocation_size(&self) -> usize125     pub fn requested_allocation_size(&self) -> usize {
126         OomOrDynError::oom_size(self.inner)
127     }
128 }
129 
130 impl From<OutOfMemory> for OomOrDynError {
from(oom: OutOfMemory) -> Self131     fn from(oom: OutOfMemory) -> Self {
132         OomOrDynError::new_oom(oom.inner)
133     }
134 }
135