folly/folly/settings/Settings.h

/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include <functional>
#include <string>
#include <vector>

#include <folly/Likely.h>
#include <folly/Range.h>
#include <folly/settings/Types.h>
#include <folly/settings/detail/SettingsImpl.h>

namespace folly {
namespace settings {

class Snapshot;
namespace detail {

/**
 * @param TrivialPtr location of the small type storage.  Optimization
 *   for better inlining.
 */
template <class T, std::atomic<uint64_t>* TrivialPtr>
class SettingWrapper {};

/**
 * Optimization: fast-path on top of the Meyers singleton. Each
 * translation unit gets this code inlined, while the slow path
 * initialization code is not.  We check the global pointer which
 * should only be initialized after the Meyers singleton. It's ok for
 * multiple calls to attempt to update the global pointer, as they
 * would be serialized on the Meyer's singleton initialization lock
 * anyway.
 *
 * Both FOLLY_SETTING_DECLARE and FOLLY_SETTING_DEFINE will provide
 * a copy of this function and we work around ODR by using different
 * overload types.
 *
 * Requires a trailing semicolon.
 */
#define FOLLY_DETAIL_SETTINGS_DEFINE_LOCAL_FUNC__(                             \
    _project, _name, _Type, _overloadType)

} // namespace detail

/**
 * Defines a setting.
 *
 * Settings are either mutable or immutable where mutable setting values can
 * change at runtime whereas immutable setting values can not be changed after
 * the setting project is frozen (see Immutables.h).
 *
 * FOLLY_SETTING_DEFINE() can only be placed in a single translation unit
 * and will be checked against accidental collisions.
 *
 * The setting API can be accessed via FOLLY_SETTING(project, name).<api_func>()
 * and is documented in the Setting class.
 *
 * All settings for a common namespace; (project, name) must be unique
 * for the whole program.  Collisions are verified at runtime on
 * program startup.
 *
 * @param _project  Project identifier, can only contain [a-zA-Z0-9]
 * @param _name  setting name within the project, can only contain [_a-zA-Z0-9].
 *   The string "<project>_<name>" must be unique for the whole program.
 * @param _Type  setting value type
 * @param _def   default value for the setting
 * @param _mut   mutability of the setting
 * @param _desc  setting documentation
 */
#define FOLLY_SETTING_DEFINE(_project, _name, _Type, _def, _mut, _desc)

/**
 * Declares a setting that's defined elsewhere.
 */
#define FOLLY_SETTING_DECLARE(_project, _name, _Type)

/**
 * Accesses a defined setting.
 * Rationale for the macro:
 *  1) Searchability, all settings access is done via FOLLY_SETTING(...)
 *  2) Prevents omitting trailing () by accident, which could
 *     lead to bugs like `auto value = *FOLLY_SETTING_project_name;`,
 *     which compiles but dereferences the function pointer instead of
 *     the setting itself.
 */
#define FOLLY_SETTING(_project, _name)

/**
 * @return If the setting exists, returns the current settings metadata.
 *         Empty Optional otherwise.
 */
Optional<SettingMetadata> getSettingsMeta(StringPiece settingName);

/**
 * @return SettingMetadata for all registered settings in the process.
 */
std::vector<SettingMetadata> getAllSettingsMeta();

namespace detail {

/**
 * Like SettingWrapper, but checks against any values saved/updated in a
 * snapshot.
 */
template <class T>
class SnapshotSettingWrapper {};

} // namespace detail

/**
 * Captures the current state of all setting values and allows
 * updating multiple settings at once, with verification and rollback.
 *
 * A single snapshot cannot be used concurrently from different
 * threads.  Multiple concurrent snapshots are safe. Passing a single
 * snapshot from one thread to another is safe as long as the user
 * properly synchronizes the handoff.
 *
 * Example usage:
 *
 *   folly::settings::Snapshot snapshot;
 *   // FOLLY_SETTING(project, name) refers to the globally visible value
 *   // snapshot(FOLLY_SETTING(project, name)) refers to the value saved in the
 *   //  snapshot
 *   FOLLY_SETTING(project, name).set(new_value);
 *   assert(*FOLLY_SETTING(project, name) == new_value);
 *   assert(*snapshot(FOLLY_SETTING(project, name)) == old_value);
 *
 *   snapshot(FOLLY_SETTING(project, name)).set(new_snapshot_value);
 *   assert(*FOLLY_SETTING(project, name) == new_value);
 *   assert(*snapshot(FOLLY_SETTING(project, name)) == new_snapshot_value);
 *
 *   // At this point we can discard the snapshot and forget new_snapshot_value,
 *   // or choose to publish:
 *   snapshot.publish();
 *   assert(*FOLLY_SETTING(project, name) == new_snapshot_value);
 */
class Snapshot final : public detail::SnapshotBase {};

namespace detail {
template <class T>
inline const T& SnapshotSettingWrapper<T>::operator*() const {}

template <class T, std::atomic<uint64_t>* TrivialPtr>
inline std::conditional_t<IsSmallPOD<T>::value, T, const T&>
SettingWrapper<T, TrivialPtr>::value(const Snapshot& snapshot) const {}

template <class T, std::atomic<uint64_t>* TrivialPtr>
StringPiece SettingWrapper<T, TrivialPtr>::updateReason(
    const Snapshot& snapshot) const {}
} // namespace detail

} // namespace settings
} // namespace folly