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 https://mozilla.org/MPL/2.0/.
4
5 //! Glean does not pass errors through the FFI component upwards.
6 //! Most errors are not propagated and simply only logged for debugging.
7 //! Errors should be recoverable and the platform-side should mostly ignore them.
8 //! Additionally the platform-side can work asynchronously anyway, leaving us with no good way to
9 //! pass back errors.
10 //!
11 //! The `HandleMapExtension` extension traits adds additional methods that log potential errors.
12 //!
13 //! **Note: the platform-side still needs to check for null pointers or other default values before
14 //! using returned values.
15 //! This is only relevant for creation of the main object and metrics as its the only things where
16 //! we return something potentially fallible.
17
18 use std::panic::UnwindSafe;
19
20 use ffi_support::{ConcurrentHandleMap, ExternError, IntoFfi};
21
handle_result<R, F>(callback: F) -> R::Value where F: UnwindSafe + FnOnce() -> Result<R, glean_core::Error>, R: IntoFfi,22 pub fn handle_result<R, F>(callback: F) -> R::Value
23 where
24 F: UnwindSafe + FnOnce() -> Result<R, glean_core::Error>,
25 R: IntoFfi,
26 {
27 let mut error = ffi_support::ExternError::success();
28 let res = ffi_support::abort_on_panic::call_with_result(&mut error, callback);
29 log_if_error(error);
30 res
31 }
32
33 /// Warns if an error occurred and then release the allocated memory.
34 ///
35 /// This is a helper for the case where we aren't exposing this back over the FFI.
36 ///
37 /// Adopted from the `consume_and_log_if_error` method, but with a changed log message.
38 ///
39 /// We assume we're not inside a `catch_unwind`, and so we wrap inside one ourselves.
log_if_error(error: ExternError)40 pub fn log_if_error(error: ExternError) {
41 if !error.get_code().is_success() {
42 // in practice this should never panic, but you never know...
43 ffi_support::abort_on_panic::call_with_output(|| {
44 log::error!(
45 "Glean failed ({:?}): {}",
46 error.get_code(),
47 error.get_message().as_str()
48 );
49 unsafe {
50 error.manually_release();
51 }
52 })
53 }
54 }
55
56 pub trait HandleMapExtension {
57 type Output;
58
59 /// Insert a newly constructed object and return a handle to it.
60 ///
61 /// This will catch and log any errors.
62 /// This will not panic on errors in the constructor.
63 ///
64 /// On success, it returns a new valid handle.
65 /// On failure, it returns the default FFI value for a handler (`0`).
insert_with_log<F>(&self, constructor: F) -> u64 where F: UnwindSafe + FnOnce() -> Result<Self::Output, glean_core::Error>66 fn insert_with_log<F>(&self, constructor: F) -> u64
67 where
68 F: UnwindSafe + FnOnce() -> Result<Self::Output, glean_core::Error>;
69
70 /// Call an infallible callback with the object identified by a handle.
71 ///
72 /// This will ignore panics.
73 ///
74 /// On success, it convert the callback return value into an FFI value and returns it.
75 /// On failure, it will return the default FFI value.
call_infallible<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&Self::Output) -> R, R: IntoFfi76 fn call_infallible<R, F>(&self, h: u64, callback: F) -> R::Value
77 where
78 F: UnwindSafe + FnOnce(&Self::Output) -> R,
79 R: IntoFfi;
80
81 /// Call an infallible callback with the object identified by a handle.
82 ///
83 /// This will ignore panics.
84 ///
85 /// On success, it convert the callback return value into an FFI value and returns it.
86 /// On failure, it will return the default FFI value.
call_infallible_mut<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&mut Self::Output) -> R, R: IntoFfi87 fn call_infallible_mut<R, F>(&self, h: u64, callback: F) -> R::Value
88 where
89 F: UnwindSafe + FnOnce(&mut Self::Output) -> R,
90 R: IntoFfi;
91
92 /// Call a callback with the object identified by a handle.
93 ///
94 /// This will catch and log any errors of the callback.
95 /// This will not panic on errors in the callback.
96 ///
97 /// On success, it convert the callback return value into an FFI value and returns it.
98 /// On failure, it will return the default FFI value.
call_with_log<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&Self::Output) -> Result<R, glean_core::Error>, R: IntoFfi99 fn call_with_log<R, F>(&self, h: u64, callback: F) -> R::Value
100 where
101 F: UnwindSafe + FnOnce(&Self::Output) -> Result<R, glean_core::Error>,
102 R: IntoFfi;
103 }
104
105 impl<T> HandleMapExtension for ConcurrentHandleMap<T> {
106 type Output = T;
107
insert_with_log<F>(&self, constructor: F) -> u64 where F: UnwindSafe + FnOnce() -> Result<Self::Output, glean_core::Error>,108 fn insert_with_log<F>(&self, constructor: F) -> u64
109 where
110 F: UnwindSafe + FnOnce() -> Result<Self::Output, glean_core::Error>,
111 {
112 let mut error = ExternError::success();
113 let res = self.insert_with_result(&mut error, constructor);
114 log_if_error(error);
115 res
116 }
117
call_infallible<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&Self::Output) -> R, R: IntoFfi,118 fn call_infallible<R, F>(&self, h: u64, callback: F) -> R::Value
119 where
120 F: UnwindSafe + FnOnce(&Self::Output) -> R,
121 R: IntoFfi,
122 {
123 let mut error = ExternError::success();
124 let res = self.call_with_output(&mut error, h, callback);
125 debug_assert!(error.get_code().is_success());
126 res
127 }
128
call_infallible_mut<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&mut Self::Output) -> R, R: IntoFfi,129 fn call_infallible_mut<R, F>(&self, h: u64, callback: F) -> R::Value
130 where
131 F: UnwindSafe + FnOnce(&mut Self::Output) -> R,
132 R: IntoFfi,
133 {
134 let mut error = ExternError::success();
135 let res = self.call_with_output_mut(&mut error, h, callback);
136 debug_assert!(error.get_code().is_success());
137 res
138 }
139
call_with_log<R, F>(&self, h: u64, callback: F) -> R::Value where F: UnwindSafe + FnOnce(&Self::Output) -> Result<R, glean_core::Error>, R: IntoFfi,140 fn call_with_log<R, F>(&self, h: u64, callback: F) -> R::Value
141 where
142 F: UnwindSafe + FnOnce(&Self::Output) -> Result<R, glean_core::Error>,
143 R: IntoFfi,
144 {
145 let mut error = ExternError::success();
146 let res = self.call_with_result(&mut error, h, callback);
147 log_if_error(error);
148 res
149 }
150 }
151