1 | /* |
2 | * Copyright 2018-present Facebook, Inc. |
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 | #pragma once |
17 | |
18 | #include <functional> |
19 | #include <string> |
20 | |
21 | #include <folly/Range.h> |
22 | #include <folly/experimental/settings/SettingsMetadata.h> |
23 | #include <folly/experimental/settings/detail/SettingsImpl.h> |
24 | |
25 | namespace folly { |
26 | namespace settings { |
27 | |
28 | class Snapshot; |
29 | namespace detail { |
30 | |
31 | /** |
32 | * @param TrivialPtr location of the small type storage. Optimization |
33 | * for better inlining. |
34 | */ |
35 | template <class T, std::atomic<uint64_t>* TrivialPtr> |
36 | class SettingWrapper { |
37 | public: |
38 | /** |
39 | * Returns the setting's current value. |
40 | * |
41 | * As an optimization, returns by value for small types, and by |
42 | * const& for larger types. Note that the returned reference is not |
43 | * guaranteed to be long-lived and should not be saved anywhere. In |
44 | * particular, a set() call might invalidate a reference obtained |
45 | * here after some amount of time (on the order of minutes). |
46 | */ |
47 | std::conditional_t<IsSmallPOD<T>::value, T, const T&> operator*() const { |
48 | return core_.getWithHint(*TrivialPtr); |
49 | } |
50 | const T* operator->() const { |
51 | return &core_.getSlow().value; |
52 | } |
53 | |
54 | /** |
55 | * Returns the setting's current value. Equivalent to dereference operator |
56 | * above. |
57 | */ |
58 | std::conditional_t<IsSmallPOD<T>::value, T, const T&> value() const { |
59 | return operator*(); |
60 | } |
61 | |
62 | /** |
63 | * Returns the setting's value from snapshot. Following two forms are |
64 | * equivalient: |
65 | * Snapshot snapshot; |
66 | * *snapshot(FOLLY_SETTING(proj, name)) == |
67 | * FOLLY_SETTING(proj, name).value(snapshot); |
68 | */ |
69 | std::conditional_t<IsSmallPOD<T>::value, T, const T&> value( |
70 | const Snapshot& snapshot) const; |
71 | |
72 | /** |
73 | * Atomically updates the setting's current value. Will invalidate |
74 | * any previous calls to operator*() after some amount of time (on |
75 | * the order of minutes). |
76 | * |
77 | * @param reason Will be stored with the current value, useful for debugging. |
78 | * @throws std::runtime_error If we can't convert t to string. |
79 | */ |
80 | void set(const T& t, StringPiece reason = "api" ) { |
81 | core_.set(t, reason); |
82 | } |
83 | |
84 | /** |
85 | * Returns the default value this setting was constructed with. |
86 | * NOTE: SettingsMetadata is type-agnostic, so it only stores the string |
87 | * representation of the default value. This method returns the |
88 | * actual value that was passed on construction. |
89 | */ |
90 | const T& defaultValue() const { |
91 | return core_.defaultValue(); |
92 | } |
93 | |
94 | explicit SettingWrapper(SettingCore<T>& core) : core_(core) {} |
95 | |
96 | private: |
97 | SettingCore<T>& core_; |
98 | friend class folly::settings::Snapshot; |
99 | }; |
100 | |
101 | /* C++20 has std::type_indentity */ |
102 | template <class T> |
103 | struct TypeIdentity { |
104 | using type = T; |
105 | }; |
106 | template <class T> |
107 | using TypeIdentityT = typename TypeIdentity<T>::type; |
108 | |
109 | /** |
110 | * Optimization: fast-path on top of the Meyers singleton. Each |
111 | * translation unit gets this code inlined, while the slow path |
112 | * initialization code is not. We check the global pointer which |
113 | * should only be initialized after the Meyers singleton. It's ok for |
114 | * multiple calls to attempt to update the global pointer, as they |
115 | * would be serialized on the Meyer's singleton initialization lock |
116 | * anyway. |
117 | * |
118 | * Both FOLLY_SETTING_DECLARE and FOLLY_SETTING_DEFINE will provide |
119 | * a copy of this function and we work around ODR by using different |
120 | * overload types. |
121 | * |
122 | * Requires a trailing semicolon. |
123 | */ |
124 | #define FOLLY_SETTINGS_DEFINE_LOCAL_FUNC__( \ |
125 | _project, _name, _Type, _overloadType) \ |
126 | extern std::atomic<folly::settings::detail::SettingCore<_Type>*> \ |
127 | FOLLY_SETTINGS_CACHE__##_project##_##_name; \ |
128 | extern std::atomic<uint64_t> FOLLY_SETTINGS_TRIVIAL__##_project##_##_name; \ |
129 | folly::settings::detail::SettingCore<_Type>& \ |
130 | FOLLY_SETTINGS_FUNC__##_project##_##_name(); \ |
131 | FOLLY_ALWAYS_INLINE auto FOLLY_SETTINGS_LOCAL_FUNC__##_project##_##_name( \ |
132 | _overloadType) { \ |
133 | if (!FOLLY_SETTINGS_CACHE__##_project##_##_name.load()) { \ |
134 | FOLLY_SETTINGS_CACHE__##_project##_##_name.store( \ |
135 | &FOLLY_SETTINGS_FUNC__##_project##_##_name()); \ |
136 | } \ |
137 | return folly::settings::detail:: \ |
138 | SettingWrapper<_Type, &FOLLY_SETTINGS_TRIVIAL__##_project##_##_name>( \ |
139 | *FOLLY_SETTINGS_CACHE__##_project##_##_name.load()); \ |
140 | } \ |
141 | /* This is here just to force a semicolon */ \ |
142 | folly::settings::detail::SettingCore<_Type>& \ |
143 | FOLLY_SETTINGS_FUNC__##_project##_##_name() |
144 | |
145 | } // namespace detail |
146 | |
147 | /** |
148 | * Defines a setting. |
149 | * |
150 | * FOLLY_SETTING_DEFINE() can only be placed in a single translation unit |
151 | * and will be checked against accidental collisions. |
152 | * |
153 | * The setting API can be accessed via FOLLY_SETTING(project, name).<api_func>() |
154 | * and is documented in the Setting class. |
155 | * |
156 | * All settings for a common namespace; (project, name) must be unique |
157 | * for the whole program. Collisions are verified at runtime on |
158 | * program startup. |
159 | * |
160 | * @param _project Project identifier, can only contain [a-zA-Z0-9] |
161 | * @param _name setting name within the project, can only contain [_a-zA-Z0-9]. |
162 | * The string "<project>_<name>" must be unique for the whole program. |
163 | * @param _Type setting value type |
164 | * @param _def default value for the setting |
165 | * @param _desc setting documentation |
166 | */ |
167 | #define FOLLY_SETTING_DEFINE(_project, _name, _Type, _def, _desc) \ |
168 | /* Fastpath optimization, see notes in FOLLY_SETTINGS_DEFINE_LOCAL_FUNC__. \ |
169 | Aggregate all off these together in a single section for better TLB \ |
170 | and cache locality. */ \ |
171 | __attribute__((__section__(".folly.settings.cache"))) \ |
172 | std::atomic<folly::settings::detail::SettingCore<_Type>*> \ |
173 | FOLLY_SETTINGS_CACHE__##_project##_##_name; \ |
174 | /* Location for the small value cache (if _Type is small and trivial). \ |
175 | Intentionally located right after the pointer cache above to take \ |
176 | advantage of the prefetching */ \ |
177 | __attribute__((__section__(".folly.settings.cache"))) std::atomic<uint64_t> \ |
178 | FOLLY_SETTINGS_TRIVIAL__##_project##_##_name; \ |
179 | /* Meyers singleton to avoid SIOF */ \ |
180 | FOLLY_NOINLINE folly::settings::detail::SettingCore<_Type>& \ |
181 | FOLLY_SETTINGS_FUNC__##_project##_##_name() { \ |
182 | static folly::Indestructible<folly::settings::detail::SettingCore<_Type>> \ |
183 | setting( \ |
184 | folly::settings::SettingMetadata{ \ |
185 | #_project, #_name, #_Type, typeid(_Type), #_def, _desc}, \ |
186 | folly::settings::detail::TypeIdentityT<_Type>{_def}, \ |
187 | FOLLY_SETTINGS_TRIVIAL__##_project##_##_name); \ |
188 | return *setting; \ |
189 | } \ |
190 | /* Ensure the setting is registered even if not used in program */ \ |
191 | auto& FOLLY_SETTINGS_INIT__##_project##_##_name = \ |
192 | FOLLY_SETTINGS_FUNC__##_project##_##_name(); \ |
193 | FOLLY_SETTINGS_DEFINE_LOCAL_FUNC__(_project, _name, _Type, char) |
194 | |
195 | /** |
196 | * Declares a setting that's defined elsewhere. |
197 | */ |
198 | #define FOLLY_SETTING_DECLARE(_project, _name, _Type) \ |
199 | FOLLY_SETTINGS_DEFINE_LOCAL_FUNC__(_project, _name, _Type, int) |
200 | |
201 | /** |
202 | * Accesses a defined setting. |
203 | * Rationale for the macro: |
204 | * 1) Searchability, all settings access is done via FOLLY_SETTING(...) |
205 | * 2) Prevents omitting trailing () by accident, which could |
206 | * lead to bugs like `auto value = *FOLLY_SETTING_project_name;`, |
207 | * which compiles but dereferences the function pointer instead of |
208 | * the setting itself. |
209 | */ |
210 | #define FOLLY_SETTING(_project, _name) \ |
211 | FOLLY_SETTINGS_LOCAL_FUNC__##_project##_##_name(0) |
212 | |
213 | /** |
214 | * @return If the setting exists, returns the current settings metadata. |
215 | * Empty Optional otherwise. |
216 | */ |
217 | Optional<SettingMetadata> getSettingsMeta(StringPiece settingName); |
218 | |
219 | namespace detail { |
220 | |
221 | /** |
222 | * Like SettingWrapper, but checks against any values saved/updated in a |
223 | * snapshot. |
224 | */ |
225 | template <class T> |
226 | class SnapshotSettingWrapper { |
227 | public: |
228 | /** |
229 | * The references are only valid for the duration of the snapshot's |
230 | * lifetime or until the setting has been updated in the snapshot, |
231 | * whichever happens earlier. |
232 | */ |
233 | const T& operator*() const; |
234 | const T* operator->() const { |
235 | return &operator*(); |
236 | } |
237 | |
238 | /** |
239 | * Update the setting in the snapshot, the effects are not visible |
240 | * in this snapshot. |
241 | */ |
242 | void set(const T& t, StringPiece reason = "api" ) { |
243 | core_.set(t, reason, &snapshot_); |
244 | } |
245 | |
246 | private: |
247 | Snapshot& snapshot_; |
248 | SettingCore<T>& core_; |
249 | friend class folly::settings::Snapshot; |
250 | |
251 | SnapshotSettingWrapper(Snapshot& snapshot, SettingCore<T>& core) |
252 | : snapshot_(snapshot), core_(core) {} |
253 | }; |
254 | |
255 | } // namespace detail |
256 | |
257 | /** |
258 | * Captures the current state of all setting values and allows |
259 | * updating multiple settings at once, with verification and rollback. |
260 | * |
261 | * A single snapshot cannot be used concurrently from different |
262 | * threads. Multiple concurrent snapshots are safe. Passing a single |
263 | * snapshot from one thread to another is safe as long as the user |
264 | * properly synchronizes the handoff. |
265 | * |
266 | * Example usage: |
267 | * |
268 | * folly::settings::Snapshot snapshot; |
269 | * // FOLLY_SETTING(project, name) refers to the globally visible value |
270 | * // snapshot(FOLLY_SETTING(project, name)) refers to the value saved in the |
271 | * // snapshot |
272 | * FOLLY_SETTING(project, name).set(new_value); |
273 | * assert(*FOLLY_SETTING(project, name) == new_value); |
274 | * assert(*snapshot(FOLLY_SETTING(project, name)) == old_value); |
275 | * |
276 | * snapshot(FOLLY_SETTING(project, name)).set(new_snapshot_value); |
277 | * assert(*FOLLY_SETTING(project, name) == new_value); |
278 | * assert(*snapshot(FOLLY_SETTING(project, name)) == new_snapshot_value); |
279 | * |
280 | * // At this point we can discard the snapshot and forget new_snapshot_value, |
281 | * // or choose to publish: |
282 | * snapshot.publish(); |
283 | * assert(*FOLLY_SETTING(project, name) == new_snapshot_value); |
284 | */ |
285 | class Snapshot final : public detail::SnapshotBase { |
286 | public: |
287 | /** |
288 | * Wraps a global FOLLY_SETTING(a, b) and returns a snapshot-local wrapper. |
289 | */ |
290 | template <class T, std::atomic<uint64_t>* P> |
291 | detail::SnapshotSettingWrapper<T> operator()( |
292 | detail::SettingWrapper<T, P>&& setting) { |
293 | return detail::SnapshotSettingWrapper<T>(*this, setting.core_); |
294 | } |
295 | |
296 | /** |
297 | * Returns a snapshot of all current setting values. |
298 | * Global settings changes will not be visible in the snapshot, and vice |
299 | * versa. |
300 | */ |
301 | Snapshot() = default; |
302 | |
303 | /** |
304 | * Apply all settings updates from this snapshot to the global state |
305 | * unconditionally. |
306 | */ |
307 | void publish() override; |
308 | |
309 | /** |
310 | * Look up a setting by name, and update the value from a string |
311 | * representation. |
312 | * |
313 | * @returns True if the setting was successfully updated, false if no setting |
314 | * with that name was found. |
315 | * @throws std::runtime_error If there's a conversion error. |
316 | */ |
317 | bool setFromString( |
318 | StringPiece settingName, |
319 | StringPiece newValue, |
320 | StringPiece reason) override; |
321 | |
322 | /** |
323 | * @return If the setting exists, the current setting information. |
324 | * Empty Optional otherwise. |
325 | */ |
326 | Optional<SettingsInfo> getAsString(StringPiece settingName) const override; |
327 | |
328 | /** |
329 | * Reset the value of the setting identified by name to its default value. |
330 | * The reason will be set to "default". |
331 | * |
332 | * @return True if the setting was reset, false if the setting is not found. |
333 | */ |
334 | bool resetToDefault(StringPiece settingName) override; |
335 | |
336 | /** |
337 | * Iterates over all known settings and calls |
338 | * func(meta, to<string>(value), reason) for each. |
339 | */ |
340 | void forEachSetting(const std::function< |
341 | void(const SettingMetadata&, StringPiece, StringPiece)>& |
342 | func) const override; |
343 | |
344 | private: |
345 | template <typename T> |
346 | friend class detail::SnapshotSettingWrapper; |
347 | |
348 | template <typename T, std::atomic<uint64_t>* TrivialPtr> |
349 | friend class detail::SettingWrapper; |
350 | }; |
351 | |
352 | namespace detail { |
353 | template <class T> |
354 | inline const T& SnapshotSettingWrapper<T>::operator*() const { |
355 | return snapshot_.get(core_).value; |
356 | } |
357 | |
358 | template <class T, std::atomic<uint64_t>* TrivialPtr> |
359 | inline std::conditional_t<IsSmallPOD<T>::value, T, const T&> |
360 | SettingWrapper<T, TrivialPtr>::value(const Snapshot& snapshot) const { |
361 | return snapshot.get(core_).value; |
362 | } |
363 | } // namespace detail |
364 | |
365 | } // namespace settings |
366 | } // namespace folly |
367 | |