1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 <initializer_list>
20 #include <new>
21 #include <stdexcept>
22 #include <type_traits>
23 
24 #include <folly/lang/SafeAssert.h>
25 #include <folly/synchronization/CallOnce.h>
26 
27 namespace folly {
28 
29 /**
30  * DelayedInit -- thread-safe delayed initialization of a value. There are two
31  * important differences between Lazy and DelayedInit:
32  *   1. DelayedInit does not store the factory function inline.
33  *   2. DelayedInit is thread-safe.
34  *
35  * Due to these differences, DelayedInit is suitable for data members. Lazy is
36  * best for local stack variables.
37  *
38  * Example Usage:
39  *
40  *   struct Foo {
41  *     Bar& bar() {
42  *       LargeState state;
43  *       return bar_.try_emplace_with(
44  *           [this, &state] { return computeBar(state); });
45  *     }
46  *    private:
47  *     Bar computeBar(LargeState&);
48  *     DelayedInit<Bar> bar_;
49  *   };
50  *
51  * If the above example were to use Lazy instead of DelayedInit:
52  *   - Storage for LargeState and this-pointer would need to be reserved in the
53  *     struct which wastes memory.
54  *   - It would require additional synchronization logic for thread-safety.
55  *
56  *
57  * Rationale:
58  *
59  *   - The stored value is initialized at most once and never deinitialized.
60  *     Unlike Lazy, the initialization logic must be provided by the consumer.
61  *     This means that DelayedInit is more of a "storage" type like
62  *     std::optional. These semantics are perfect for thread-safe, lazy
63  *     initialization of a data member.
64  *
65  *   - DelayedInit models neither MoveConstructible nor CopyConstructible. The
66  *     rationale is the same as that of std::once_flag.
67  *
68  *   - There is no need for a non-thread-safe version of DelayedInit.
69  *     std::optional will suffice in these cases.
70  */
71 template <typename T>
72 struct DelayedInit {
73   DelayedInit() = default;
74   DelayedInit(const DelayedInit&) = delete;
75   DelayedInit& operator=(const DelayedInit&) = delete;
76 
77   /**
78    * Gets the pre-existing value if already initialized or creates the value
79    * returned by the provided factory function. If the value already exists,
80    * then the provided function is not called.
81    */
82   template <typename Func>
try_emplace_withDelayedInit83   T& try_emplace_with(Func func) {
84     auto addr = static_cast<void*>(std::addressof(storage_.value));
85     call_once(storage_.init, [&] { ::new (addr) T(func()); });
86     return storage_.value;
87   }
88 
89   /**
90    * Gets the pre-existing value if already initialized or constructs the value
91    * in-place by direct-initializing with the provided arguments.
92    */
93   template <typename... A>
try_emplaceDelayedInit94   T& try_emplace(A&&... a) {
95     return try_emplace_with([&] { return T(static_cast<A&&>(a)...); });
96   }
97   template <
98       typename U,
99       typename... A,
100       typename = std::enable_if_t<
101           std::is_constructible<T, std::initializer_list<U>, A...>::value>>
try_emplaceDelayedInit102   T& try_emplace(std::initializer_list<U> ilist, A&&... a) {
103     return try_emplace_with([&] { return T(ilist, static_cast<A&&>(a)...); });
104   }
105 
has_valueDelayedInit106   bool has_value() const { return test_once(storage_.init); }
107   explicit operator bool() const { return has_value(); }
108 
valueDelayedInit109   T& value() {
110     require_value();
111     return storage_.value;
112   }
113 
valueDelayedInit114   const T& value() const {
115     require_value();
116     return storage_.value;
117   }
118 
119   T& operator*() {
120     FOLLY_SAFE_DCHECK(has_value(), "tried to access empty DelayedInit");
121     return storage_.value;
122   }
123   const T& operator*() const {
124     FOLLY_SAFE_DCHECK(has_value(), "tried to access empty DelayedInit");
125     return storage_.value;
126   }
127   T* operator->() {
128     FOLLY_SAFE_DCHECK(has_value(), "tried to access empty DelayedInit");
129     return std::addressof(storage_.value);
130   }
131   const T* operator->() const {
132     FOLLY_SAFE_DCHECK(has_value(), "tried to access empty DelayedInit");
133     return std::addressof(storage_.value);
134   }
135 
136  private:
require_valueDelayedInit137   void require_value() const {
138     if (!has_value()) {
139       throw_exception<std::logic_error>("tried to access empty DelayedInit");
140     }
141   }
142 
143   using OnceFlag = std::conditional_t<
144       alignof(T) >= sizeof(once_flag),
145       once_flag,
146       compact_once_flag>;
147 
148   struct StorageTriviallyDestructible {
149     union {
150       std::remove_const_t<T> value;
151     };
152     OnceFlag init;
153 
StorageTriviallyDestructibleDelayedInit::StorageTriviallyDestructible154     StorageTriviallyDestructible() {}
155   };
156 
157   struct StorageNonTriviallyDestructible {
158     union {
159       std::remove_const_t<T> value;
160     };
161     OnceFlag init;
162 
StorageNonTriviallyDestructibleDelayedInit::StorageNonTriviallyDestructible163     StorageNonTriviallyDestructible() {}
~StorageNonTriviallyDestructibleDelayedInit::StorageNonTriviallyDestructible164     ~StorageNonTriviallyDestructible() {
165       if (test_once(this->init)) {
166         this->value.~T();
167       }
168     }
169   };
170 
171   using Storage = std::conditional_t<
172       std::is_trivially_destructible<T>::value,
173       StorageTriviallyDestructible,
174       StorageNonTriviallyDestructible>;
175 
176   Storage storage_;
177 };
178 
179 } // namespace folly
180