Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/base/SkNoDestructor.h
Line
Count
Source
1
/*
2
 * Copyright 2018 Google LLC
3
 *
4
 * Use of this source code is governed by a BSD-style license that can be
5
 * found in the LICENSE file.
6
 */
7
#ifndef SkNoDestructor_DEFINED
8
#define SkNoDestructor_DEFINED
9
10
#include <cstddef>
11
#include <new>
12
#include <type_traits>  // IWYU pragma: keep
13
#include <utility>
14
15
// Helper type to create a function-local static variable of type `T` when `T`
16
// has a non-trivial destructor. Storing a `T` in a `SkNoDestructor<T>` will
17
// prevent `~T()` from running, even when the variable goes out of scope. This
18
// code is adapted from `base::NoDestructor<T>` in Chromium.
19
//
20
// Useful when a variable has static storage duration but its type has a
21
// non-trivial destructor. Chromium (and transitively, Skia) bans global
22
// constructors and destructors: using a function-local static variable prevents
23
// the former, while using `SkNoDestructor<T>` prevents the latter.
24
//
25
// ## Caveats
26
//
27
// - Must not be used for locals or fields; by definition, this does not run
28
//   destructors, and this will likely lead to memory leaks and other
29
//   surprising and undesirable behaviour.
30
//
31
// - If `T` is not constexpr constructible, must be a function-local static
32
//   variable, since a global `NoDestructor<T>` will still generate a static
33
//   initializer.
34
//
35
// - If `T` is constinit constructible, may be used as a global, but mark the
36
//   global `constinit` (once C++20 is available)
37
//
38
// - If the data is rarely used, consider creating it on demand rather than
39
//   caching it for the lifetime of the program. Though `SkNoDestructor<T>`
40
//   does not heap allocate, the compiler still reserves space in bss for
41
//   storing `T`, which costs memory at runtime.
42
//
43
// - If `T` is trivially destructible, do not use `SkNoDestructor<T>`:
44
//
45
//     const uint64_t GetUnstableSessionSeed() {
46
//       // No need to use `SkNoDestructor<T>` as `uint64_t` is trivially
47
//       // destructible and does not require a global destructor.
48
//       static const uint64_t kSessionSeed = GetRandUint64();
49
//       return kSessionSeed;
50
//     }
51
//
52
// ## Example Usage
53
//
54
// const std::string& GetDefaultText() {
55
//   // Required since `static const std::string` requires a global destructor.
56
//   static const SkNoDestructor<std::string> s("Hello world!");
57
//   return *s;
58
// }
59
//
60
// More complex initialization using a lambda:
61
//
62
// const std::string& GetRandomNonce() {
63
//   // `nonce` is initialized with random data the first time this function is
64
//   // called, but its value is fixed thereafter.
65
//   static const SkNoDestructor<std::string> nonce([] {
66
//     std::string s(16);
67
//     GetRandString(s.data(), s.size());
68
//     return s;
69
//   }());
70
//   return *nonce;
71
// }
72
//
73
// ## Thread safety
74
//
75
// Initialization of function-local static variables is thread-safe since C++11.
76
// The standard guarantees that:
77
//
78
// - function-local static variables will be initialised the first time
79
//   execution passes through the declaration.
80
//
81
// - if another thread's execution concurrently passes through the declaration
82
//   in the middle of initialisation, that thread will wait for the in-progress
83
//   initialisation to complete.
84
template <typename T> class SkNoDestructor {
85
public:
86
    static_assert(!(std::is_trivially_constructible_v<T> && std::is_trivially_destructible_v<T>),
87
                  "T is trivially constructible and destructible; please use a constinit object of "
88
                  "type T directly instead");
89
90
    static_assert(!std::is_trivially_destructible_v<T>,
91
                  "T is trivially destructible; please use a function-local static of type T "
92
                  "directly instead");
93
94
    // Not constexpr; just write static constexpr T x = ...; if the value should be a constexpr.
95
302
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
302
        new (fStorage) T(std::forward<Args>(args)...);
97
302
    }
SkNoDestructor<SkBlendModeBlender>::SkNoDestructor<SkBlendMode>(SkBlendMode&&)
Line
Count
Source
95
262
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
262
        new (fStorage) T(std::forward<Args>(args)...);
97
262
    }
Unexecuted instantiation: SkNoDestructor<SkTrivialExecutor>::SkNoDestructor<>()
SkNoDestructor<SkMutex>::SkNoDestructor<>()
Line
Count
Source
95
3
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
3
        new (fStorage) T(std::forward<Args>(args)...);
97
3
    }
SkNoDestructor<SkLRUCache<unsigned long, sk_sp<SkRuntimeEffect>, SkGoodHash> >::SkNoDestructor<int>(int&&)
Line
Count
Source
95
3
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
3
        new (fStorage) T(std::forward<Args>(args)...);
97
3
    }
SkTypeface.cpp:SkNoDestructor<(anonymous namespace)::SkEmptyTypeface>::SkNoDestructor<>()
Line
Count
Source
95
11
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
11
        new (fStorage) T(std::forward<Args>(args)...);
97
11
    }
SkNoDestructor<std::__1::vector<SkCodecs::Decoder, std::__1::allocator<SkCodecs::Decoder> > >::SkNoDestructor<>()
Line
Count
Source
95
7
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
7
        new (fStorage) T(std::forward<Args>(args)...);
97
7
    }
SkNoDestructor<SkSL::ModuleLoader::Impl>::SkNoDestructor<>()
Line
Count
Source
95
8
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
8
        new (fStorage) T(std::forward<Args>(args)...);
97
8
    }
SkNoDestructor<sktext::gpu::DistanceFieldAdjustTable>::SkNoDestructor<>()
Line
Count
Source
95
1
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
1
        new (fStorage) T(std::forward<Args>(args)...);
97
1
    }
SkNoDestructor<SkColorSpaceXformColorFilter>::SkNoDestructor<sk_sp<SkColorSpace>, sk_sp<SkColorSpace> >(sk_sp<SkColorSpace>&&, sk_sp<SkColorSpace>&&)
Line
Count
Source
95
7
    template <typename... Args> explicit SkNoDestructor(Args&&... args) {
96
7
        new (fStorage) T(std::forward<Args>(args)...);
97
7
    }
98
99
    // Allows copy and move construction of the contained type, to allow construction from an
100
    // initializer list, e.g. for std::vector.
101
    explicit SkNoDestructor(const T& x) { new (fStorage) T(x); }
102
100
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
SkTypeface.cpp:SkNoDestructor<std::__1::vector<(anonymous namespace)::DecoderProc, std::__1::allocator<(anonymous namespace)::DecoderProc> > >::SkNoDestructor(std::__1::vector<(anonymous namespace)::DecoderProc, std::__1::allocator<(anonymous namespace)::DecoderProc> >&&)
Line
Count
Source
102
74
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::LayoutFlag, SkGoodHash> >::SkNoDestructor(skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::LayoutFlag, SkGoodHash>&&)
Line
Count
Source
102
8
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
SkNoDestructor<SkSL::String::Separator()::Output>::SkNoDestructor(SkSL::String::Separator()::Output&&)
Line
Count
Source
102
8
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, bool const SkSL::ShaderCaps::*, SkGoodHash> >::SkNoDestructor(skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, bool const SkSL::ShaderCaps::*, SkGoodHash>&&)
Line
Count
Source
102
2
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::IntrinsicKind, SkGoodHash> >::SkNoDestructor(skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::IntrinsicKind, SkGoodHash>&&)
Line
Count
Source
102
8
    explicit SkNoDestructor(T&& x) { new (fStorage) T(std::move(x)); }
103
104
    SkNoDestructor(const SkNoDestructor&) = delete;
105
    SkNoDestructor& operator=(const SkNoDestructor&) = delete;
106
107
    ~SkNoDestructor() = default;
108
109
35.7k
    const T& operator*() const { return *get(); }
110
28.9k
    T& operator*() { return *get(); }
Unexecuted instantiation: SkNoDestructor<SkTrivialExecutor>::operator*()
SkNoDestructor<SkMutex>::operator*()
Line
Count
Source
110
9.37k
    T& operator*() { return *get(); }
SkNoDestructor<SkSL::ModuleLoader::Impl>::operator*()
Line
Count
Source
110
19.2k
    T& operator*() { return *get(); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, bool const SkSL::ShaderCaps::*, SkGoodHash> >::operator*()
Line
Count
Source
110
325
    T& operator*() { return *get(); }
111
112
6.73k
    const T* operator->() const { return get(); }
113
9.44k
    T* operator->() { return get(); }
SkNoDestructor<SkLRUCache<unsigned long, sk_sp<SkRuntimeEffect>, SkGoodHash> >::operator->()
Line
Count
Source
113
9.37k
    T* operator->() { return get(); }
SkNoDestructor<std::__1::vector<SkCodecs::Decoder, std::__1::allocator<SkCodecs::Decoder> > >::operator->()
Line
Count
Source
113
70
    T* operator->() { return get(); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::LayoutFlag, SkGoodHash> >::operator->()
Line
Count
Source
113
6
    T* operator->() { return get(); }
114
115
42.5k
    const T* get() const { return reinterpret_cast<const T*>(fStorage); }
SkNoDestructor<SkSL::String::Separator()::Output>::get() const
Line
Count
Source
115
6.73k
    const T* get() const { return reinterpret_cast<const T*>(fStorage); }
SkNoDestructor<sktext::gpu::DistanceFieldAdjustTable>::get() const
Line
Count
Source
115
70
    const T* get() const { return reinterpret_cast<const T*>(fStorage); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::IntrinsicKind, SkGoodHash> >::get() const
Line
Count
Source
115
35.7k
    const T* get() const { return reinterpret_cast<const T*>(fStorage); }
116
46.4M
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<SkBlendModeBlender>::get()
Line
Count
Source
116
587k
    T* get() { return reinterpret_cast<T*>(fStorage); }
Unexecuted instantiation: SkNoDestructor<SkTrivialExecutor>::get()
SkNoDestructor<SkMutex>::get()
Line
Count
Source
116
9.37k
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<SkLRUCache<unsigned long, sk_sp<SkRuntimeEffect>, SkGoodHash> >::get()
Line
Count
Source
116
9.37k
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkTypeface.cpp:SkNoDestructor<(anonymous namespace)::SkEmptyTypeface>::get()
Line
Count
Source
116
45.0M
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkTypeface.cpp:SkNoDestructor<std::__1::vector<(anonymous namespace)::DecoderProc, std::__1::allocator<(anonymous namespace)::DecoderProc> > >::get()
Line
Count
Source
116
583k
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<std::__1::vector<SkCodecs::Decoder, std::__1::allocator<SkCodecs::Decoder> > >::get()
Line
Count
Source
116
168k
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<SkSL::ModuleLoader::Impl>::get()
Line
Count
Source
116
19.2k
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, SkSL::LayoutFlag, SkGoodHash> >::get()
Line
Count
Source
116
6
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<skia_private::THashMap<std::__1::basic_string_view<char, std::__1::char_traits<char> >, bool const SkSL::ShaderCaps::*, SkGoodHash> >::get()
Line
Count
Source
116
325
    T* get() { return reinterpret_cast<T*>(fStorage); }
SkNoDestructor<SkColorSpaceXformColorFilter>::get()
Line
Count
Source
116
65
    T* get() { return reinterpret_cast<T*>(fStorage); }
117
118
private:
119
    alignas(T) std::byte fStorage[sizeof(T)];
120
121
#if defined(__clang__) && defined(__has_feature)
122
#if __has_feature(leak_sanitizer) || __has_feature(address_sanitizer)
123
    // TODO(https://crbug.com/812277): This is a hack to work around the fact that LSan doesn't seem
124
    // to treat SkNoDestructor as a root for reachability analysis. This means that code like this:
125
    //     static SkNoDestructor<std::vector<int>> v({1, 2, 3});
126
    // is considered a leak. Using the standard leak sanitizer annotations to suppress leaks doesn't
127
    // work: std::vector is implicitly constructed before calling the SkNoDestructor constructor.
128
    //
129
    // Unfortunately, I haven't been able to demonstrate this issue in simpler reproductions: until
130
    // that's resolved, hold an explicit pointer to the placement-new'd object in leak sanitizer
131
    // mode to help LSan realize that objects allocated by the contained type are still reachable.
132
    T* fStoragePtr = reinterpret_cast<T*>(fStorage);
133
#endif  // leak_sanitizer/address_sanitizer
134
#endif  // __has_feature
135
};
136
137
#endif  // SkNoDestructor_DEFINED