error.rs (7077B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! Types needed to marshal [`server`](crate::server) errors back to C++ in Firefox. The main type 6 //! of this module is [`ErrorBuffer`](crate::server::ErrorBuffer). 7 8 use std::{ 9 borrow::Cow, 10 error::Error, 11 fmt::{self, Display, Formatter}, 12 os::raw::c_char, 13 ptr, 14 }; 15 16 use nsstring::nsCString; 17 use serde::{Deserialize, Serialize}; 18 use wgc::id; 19 use wgt::error::{ErrorType, WebGpuError}; 20 21 /// A non-owning representation of `mozilla::webgpu::ErrorBuffer` in C++, passed as an argument to 22 /// other functions in [this module](self). 23 /// 24 /// C++ callers of Rust functions (presumably in `WebGPUParent.cpp`) that expect one of these 25 /// structs can create a `mozilla::webgpu::ErrorBuffer` object, and call its `ToFFI` method to 26 /// construct a value of this type, available to C++ as `mozilla::webgpu::ffi::WGPUErrorBuffer`. If 27 /// we catch a `Result::Err` in other functions of [this module](self), the error is converted to 28 /// this type. 29 #[repr(C)] 30 pub struct ErrorBuffer { 31 /// The type of error that `string` is associated with. If this location is set to 32 /// [`ErrorBufferType::None`] after being passed as an argument to a function in [this module](self), 33 /// then the remaining fields are guaranteed to not have been altered by that function from 34 /// their original state. 35 r#type: *mut ErrorBufferType, 36 /// The (potentially truncated) message associated with this error. A fixed-capacity, 37 /// null-terminated UTF-8 string buffer owned by C++. 38 /// 39 /// When we convert WGPU errors to this type, we render the error as a string, copying into 40 /// `message` up to `capacity - 1`, and null-terminate it. 41 message: *mut c_char, 42 message_capacity: usize, 43 device_id: *mut wgc::id::DeviceId, 44 } 45 46 impl ErrorBuffer { 47 /// Fill this buffer with the textual representation of `error`. 48 /// 49 /// If the error message is too long, truncate it to `self.capacity`. In either case, the error 50 /// message is always terminated by a zero byte. 51 /// 52 /// Note that there is no explicit indication of the message's length, only the terminating zero 53 /// byte. If the textual form of `error` itself includes a zero byte (as Rust strings can), then 54 /// the C++ code receiving this error message has no way to distinguish that from the 55 /// terminating zero byte, and will see the message as shorter than it is. 56 pub(crate) fn init(&mut self, error: impl WebGpuError, device_id: wgc::id::DeviceId) { 57 let err_ty = match error.webgpu_error_type() { 58 ErrorType::Internal => ErrorBufferType::Internal, 59 ErrorType::OutOfMemory => ErrorBufferType::OutOfMemory, 60 ErrorType::Validation => ErrorBufferType::Validation, 61 ErrorType::DeviceLost => return, // will be surfaced via callback 62 }; 63 // SAFETY: We presume the pointer provided by the caller is safe to write to. 64 unsafe { *self.r#type = err_ty }; 65 66 unsafe { *self.device_id = device_id }; 67 68 let message = error_to_string(&error); 69 70 assert_ne!(self.message_capacity, 0); 71 // Since we need to store a nul terminator after the content, the 72 // content length must always be strictly less than the buffer's 73 // capacity. 74 let length = if message.len() >= self.message_capacity { 75 // Thanks to the structure of UTF-8, `std::is_char_boundary` is 76 // O(1), so this should examine a few bytes at most. 77 // 78 // The largest value in this range is `self.message_capacity - 1`, 79 // which is a safe length. 80 let truncated_length = (0..self.message_capacity) 81 .rfind(|&offset| message.is_char_boundary(offset)) 82 .unwrap_or(0); 83 log::warn!( 84 "Error message's length {} reached capacity {}, truncating to {}", 85 message.len(), 86 self.message_capacity, 87 truncated_length, 88 ); 89 truncated_length 90 } else { 91 message.len() 92 }; 93 unsafe { 94 ptr::copy_nonoverlapping(message.as_ptr(), self.message as *mut u8, length); 95 *self.message.add(length) = 0; 96 } 97 } 98 } 99 100 pub struct OwnedErrorBuffer { 101 device_id: Option<id::DeviceId>, 102 ty: ErrorBufferType, 103 message: nsCString, 104 } 105 106 impl OwnedErrorBuffer { 107 pub fn new() -> Self { 108 Self { 109 device_id: None, 110 ty: ErrorBufferType::None, 111 message: nsCString::new(), 112 } 113 } 114 115 pub(crate) fn init(&mut self, error: impl WebGpuError, device_id: id::DeviceId) { 116 assert!(self.device_id.is_none()); 117 118 let ty = match error.webgpu_error_type() { 119 ErrorType::Internal => ErrorBufferType::Internal, 120 ErrorType::OutOfMemory => ErrorBufferType::OutOfMemory, 121 ErrorType::Validation => ErrorBufferType::Validation, 122 ErrorType::DeviceLost => return, // will be surfaced via callback 123 }; 124 125 self.device_id = Some(device_id); 126 self.ty = ty; 127 128 let message = error_to_string(&error); 129 self.message = nsCString::from(message); 130 } 131 132 pub(crate) fn get_inner_data(&self) -> Option<(id::DeviceId, ErrorBufferType, &nsCString)> { 133 Some((self.device_id?, self.ty, &self.message)) 134 } 135 } 136 137 pub fn error_to_string(error: impl Error) -> String { 138 use std::fmt::Write; 139 let mut message = format!("{}", error); 140 let mut e = error.source(); 141 while let Some(source) = e { 142 write!(message, ", caused by: {}", source).unwrap(); 143 e = source.source(); 144 } 145 message 146 } 147 148 /// Corresponds to an optional discriminant of [`GPUError`] type in the WebGPU API. Strongly 149 /// correlates to [`GPUErrorFilter`]s. 150 /// 151 /// [`GPUError`]: https://gpuweb.github.io/gpuweb/#gpuerror 152 /// [`GPUErrorFilter`]: https://gpuweb.github.io/gpuweb/#enumdef-gpuerrorfilter 153 #[repr(u8)] 154 #[derive(Clone, Copy, Debug, Deserialize, Serialize)] 155 pub(crate) enum ErrorBufferType { 156 None = 0, 157 DeviceLost = 1, 158 Internal = 2, 159 OutOfMemory = 3, 160 Validation = 4, 161 } 162 163 /// Representation an error whose error message is already rendered as a [`&str`], and has no error 164 /// sources. Used for convenience in [`server`](crate::server) code. 165 #[derive(Clone, Debug)] 166 pub(crate) struct ErrMsg { 167 pub(crate) message: Cow<'static, str>, 168 pub(crate) r#type: ErrorType, 169 } 170 171 impl ErrMsg { 172 pub fn oom() -> Self { 173 Self { 174 message: "Out of memory".into(), 175 r#type: ErrorType::OutOfMemory, 176 } 177 } 178 } 179 180 impl Display for ErrMsg { 181 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 182 let Self { message, r#type: _ } = self; 183 write!(f, "{message}") 184 } 185 } 186 187 impl Error for ErrMsg {} 188 189 impl WebGpuError for ErrMsg { 190 fn webgpu_error_type(&self) -> ErrorType { 191 self.r#type 192 } 193 }