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
25namespace folly {
26namespace settings {
27
28class Snapshot;
29namespace detail {
30
31/**
32 * @param TrivialPtr location of the small type storage. Optimization
33 * for better inlining.
34 */
35template <class T, std::atomic<uint64_t>* TrivialPtr>
36class 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 */
102template <class T>
103struct TypeIdentity {
104 using type = T;
105};
106template <class T>
107using 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 */
217Optional<SettingMetadata> getSettingsMeta(StringPiece settingName);
218
219namespace detail {
220
221/**
222 * Like SettingWrapper, but checks against any values saved/updated in a
223 * snapshot.
224 */
225template <class T>
226class 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 */
285class 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
352namespace detail {
353template <class T>
354inline const T& SnapshotSettingWrapper<T>::operator*() const {
355 return snapshot_.get(core_).value;
356}
357
358template <class T, std::atomic<uint64_t>* TrivialPtr>
359inline std::conditional_t<IsSmallPOD<T>::value, T, const T&>
360SettingWrapper<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