• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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