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