1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <incfs_support/signal_handling.h> 20 21 #include <optional> 22 #include <type_traits> 23 24 namespace incfs { 25 26 // Couple helper templates for access() implementation 27 template <class Arg, class Func> 28 using func_result = 29 std::remove_cvref_t<decltype(std::declval<Func>()(std::declval<Arg>()))>; 30 31 template <class Arg, class Func> 32 constexpr auto is_void_func = std::is_same_v<func_result<Arg, Func>, void>; 33 34 template <class Arg, class Func> 35 using optional_result = 36 std::conditional_t<is_void_func<Arg, Func>, bool, 37 std::optional<func_result<Arg, Func>>>; 38 39 // 40 // A safer alternative for the SCOPED_SIGBUS_HANDLER() macro. 41 // 42 // This function accepts a closure, which is a block of code that may generate 43 // an error while accessing mapped memory. By using access() function you limit 44 // the potential for leaking resources to only the code in the closure. Anything 45 // allocated before, or after the call, works exactly as expected by the C++ 46 // standards, and will get its destructors run even in case of access error. But 47 // the code inside the closure still has the same issues as with the macros - no 48 // cleanup happens if reading memory fails. That's why it's better to not create 49 // any new objects inside of it, and not call external code, especially the one 50 // you don't control. 51 // 52 // While it's a safer alternative, one may still need to use the functions from 53 // incfs_support/util.h to ensure a proper memory cleanup. 54 // 55 // Code generated from the access() call is very similar to the raw macro, but 56 // sometimes the optimizer decides to make accessor() a real function call 57 // instead of inlining it. This is the only piece of overhead you may get, and 58 // that's why best practice is to start with this function first. Only fall back 59 // to SCOPED_SIGBUS_HANDLER() macro if it gives MEASURABLE improvement. 60 // 61 // @param ptr - mapped memory pointer to access 62 // @param accessor - a closure that runs under failure protection. Its return 63 // value wrapped into 64 // std::optional is the whole function return value 65 // @return - std::optional(access(ptr)) if no error happened, or 66 // std::nullopt on error; 67 // for a void-returning accessor a bool indicating if access 68 // was successful. 69 70 template <class Ptr, class F> 71 auto access(Ptr ptr, F&& accessor) -> optional_result<Ptr, F> { 72 SCOPED_SIGBUS_HANDLER({ return {}; }); 73 74 if constexpr (is_void_func<Ptr, F>) { 75 accessor(ptr); 76 return true; 77 } else { 78 return accessor(ptr); 79 } 80 } 81 82 } // namespace incfs