Skip to content

Commit

Permalink
Add API to get an Observer from a setting
Browse files Browse the repository at this point in the history
Summary: Use setting update callbacks to support getting setting value observers

Reviewed By: yfeldblum

Differential Revision: D67355069

fbshipit-source-id: 1d4e452111803b51a129c7d7f4eab73ff52eb297
  • Loading branch information
Kevin Doherty authored and facebook-github-bot committed Dec 18, 2024
1 parent 8a0687a commit 2587ce2
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
12 changes: 12 additions & 0 deletions folly/settings/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ cpp_library(
],
)

cpp_library(
name = "observer",
headers = [
"Observer.h",
],
exported_deps = [
":settings",
"//folly/observer:observer",
"//folly/observer:simple_observable",
],
)

cpp_library(
name = "types",
srcs = [
Expand Down
54 changes: 54 additions & 0 deletions folly/settings/Observer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 <atomic>
#include <memory>

#include <folly/observer/Observer.h>
#include <folly/observer/SimpleObservable.h>
#include <folly/settings/Settings.h>

namespace folly::settings {
/*
* Get a folly::observer::Observer<T> for a given setting. For example:
* folly::settings::getObserver(FOLLY_SETTING(project, retention))
*
* Useful for cases which may want to trigger callbacks when settings are
* updated, or for cases that are already built on top of Observers.
*
* The returned Observer is updated whenever the setting is updated.
*/
template <typename T, std::atomic<uint64_t>* Ptr, typename Tag>
observer::Observer<T> getObserver(
settings::detail::SettingWrapper<T, Ptr, Tag> setting) {
// Make observable a unique_ptr so it can be moved and captured in the setting
// update callback
auto observable = std::make_unique<observer::SimpleObservable<T>>(*setting);
auto observer = observable->getObserver();

auto callbackHandle = setting.addCallback(
[observable = std::move(observable)](const auto& newContents) {
observable->setValue(newContents.value);
});
// Create a wrapped observer to capture the callback handle and keep it alive
// as long as the observer is alive
return observer::makeObserver(
[callbackHandle = std::move(callbackHandle),
observer = std::move(observer)]() { return **observer; });
}
} // namespace folly::settings
2 changes: 2 additions & 0 deletions folly/settings/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ cpp_unittest(
":b",
"//folly:format",
"//folly:string",
"//folly/experimental/observer/detail:observer_manager",
"//folly/portability:gmock",
"//folly/portability:gtest",
"//folly/settings:observer",
"//folly/settings:settings",
"//folly/synchronization/test:barrier",
],
Expand Down
18 changes: 18 additions & 0 deletions folly/settings/test/SettingsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@

#include <folly/Format.h>
#include <folly/String.h>
#include <folly/experimental/observer/detail/ObserverManager.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
#include <folly/settings/Observer.h>
#include <folly/synchronization/test/Barrier.h>

#include <folly/settings/test/a.h>
Expand Down Expand Up @@ -645,6 +647,22 @@ TEST(SettingsTest, callback) {
EXPECT_EQ(lastCallbackValue, "d");
}

TEST(SettingsTest, observers) {
auto observer = folly::settings::getObserver(
some_ns::FOLLY_SETTING(follytest, some_flag));
std::string updatedFromCallback;
auto callbackHandle = observer.addCallback([&](auto snapshot) {
updatedFromCallback = *snapshot;
});
EXPECT_EQ(**observer, "default");
EXPECT_EQ(updatedFromCallback, "default");

some_ns::FOLLY_SETTING(follytest, some_flag).set("new value");
folly::observer_detail::ObserverManager::waitForAllUpdates();
EXPECT_EQ(**observer, "new value");
EXPECT_EQ(updatedFromCallback, "new value");
}

TEST(Settings, immutables) {
EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, immutable_setting)->value_, 100);
EXPECT_EQ(*some_ns::FOLLY_SETTING(otherproj, some_flag), "default");
Expand Down

0 comments on commit 2587ce2

Please sign in to comment.