First stab at config framework (#27014)

* First stab at config framework

* Automated change: Fix sanity tests

* add test for threaded initialization

* better test

* clang format

* better filenames

* Automated change: Fix sanity tests

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/26912/head^2
Craig Tiller 3 years ago committed by GitHub
parent f5d3ed2db1
commit cad2255d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      BUILD
  2. 36
      CMakeLists.txt
  3. 11
      build_autogenerated.yaml
  4. 53
      src/core/lib/config/core_configuration.cc
  5. 74
      src/core/lib/config/core_configuration.h
  6. 31
      test/core/config/BUILD
  7. 71
      test/core/config/core_configuration_test.cc
  8. 24
      tools/run_tests/generated/tests.json

14
BUILD

@ -783,6 +783,20 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "config",
srcs = [
"src/core/lib/config/core_configuration.cc",
],
language = "c++",
public_hdrs = [
"src/core/lib/config/core_configuration.h",
],
deps = [
"gpr_platform",
],
)
grpc_cc_library(
name = "debug_location",
language = "c++",

36
CMakeLists.txt generated

@ -843,6 +843,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx connectivity_state_test)
add_dependencies(buildtests_cxx context_allocator_end2end_test)
add_dependencies(buildtests_cxx context_list_test)
add_dependencies(buildtests_cxx core_configuration_test)
add_dependencies(buildtests_cxx delegating_channel_test)
add_dependencies(buildtests_cxx destroy_grpclb_channel_with_active_connect_stress_test)
add_dependencies(buildtests_cxx dual_ref_counted_test)
@ -10233,6 +10234,41 @@ target_link_libraries(context_list_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(core_configuration_test
src/core/lib/config/core_configuration.cc
test/core/config/core_configuration_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_include_directories(core_configuration_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(core_configuration_test
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
)
endif()
if(gRPC_BUILD_TESTS)

@ -5119,6 +5119,17 @@ targets:
deps:
- grpc_test_util
uses_polling: false
- name: core_configuration_test
gtest: true
build: test
language: c++
headers:
- src/core/lib/config/core_configuration.h
src:
- src/core/lib/config/core_configuration.cc
- test/core/config/core_configuration_test.cc
deps: []
uses_polling: false
- name: delegating_channel_test
gtest: true
build: test

@ -0,0 +1,53 @@
// Copyright 2021 gRPC authors.
//
// 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.
#include <grpc/impl/codegen/port_platform.h>
#include "src/core/lib/config/core_configuration.h"
namespace grpc_core {
std::atomic<CoreConfiguration*> CoreConfiguration::config_;
CoreConfiguration::Builder::Builder() = default;
CoreConfiguration* CoreConfiguration::Builder::Build() {
return new CoreConfiguration;
}
CoreConfiguration::CoreConfiguration() = default;
const CoreConfiguration& CoreConfiguration::BuildNewAndMaybeSet() {
// Construct builder, pass it up to code that knows about build configuration
Builder builder;
BuildCoreConfiguration(&builder);
// Use builder to construct a confguration
CoreConfiguration* p = builder.Build();
// Try to set configuration global - it's possible another thread raced us
// here, in which case we drop the work we did and use the one that got set
// first
CoreConfiguration* expected = nullptr;
if (!config_.compare_exchange_strong(expected, p,
std::memory_order_release)) {
delete p;
return *expected;
}
return *p;
}
void CoreConfiguration::Reset() {
delete config_.exchange(nullptr, std::memory_order_acquire);
}
} // namespace grpc_core

@ -0,0 +1,74 @@
// Copyright 2021 gRPC authors.
//
// 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.
#ifndef GRPC_CORE_LIB_CONFIG_CORE_CONFIGURATION_H
#define GRPC_CORE_LIB_CONFIG_CORE_CONFIGURATION_H
#include <grpc/impl/codegen/port_platform.h>
#include <atomic>
namespace grpc_core {
// Global singleton that stores library configuration - factories, etc...
// that plugins might choose to extend.
class CoreConfiguration {
public:
CoreConfiguration(const CoreConfiguration&) = delete;
CoreConfiguration& operator=(const CoreConfiguration&) = delete;
// Builder is passed to plugins, etc... at initialization time to collect
// their configuration and assemble the published CoreConfiguration.
class Builder {
public:
private:
friend class CoreConfiguration;
Builder();
CoreConfiguration* Build();
};
// Lifetime methods
// Get the core configuration; if it does not exist, create it.
static const CoreConfiguration& Get() {
CoreConfiguration* p = config_.load(std::memory_order_acquire);
if (p != nullptr) {
return *p;
}
return BuildNewAndMaybeSet();
}
// Drop the core configuration. Users must ensure no other threads are
// accessing the configuration.
static void Reset();
// Accessors
private:
CoreConfiguration();
// Create a new CoreConfiguration, and either set it or throw it away.
// We allow multiple CoreConfiguration's to be created in parallel.
static const CoreConfiguration& BuildNewAndMaybeSet();
// The configuration
static std::atomic<CoreConfiguration*> config_;
};
extern void BuildCoreConfiguration(CoreConfiguration::Builder* builder);
} // namespace grpc_core
#endif /* GRPC_CORE_LIB_CONFIG_CORE_CONFIGURATION_H */

@ -0,0 +1,31 @@
# Copyright 2021 gRPC authors.
#
# 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.
load("//bazel:grpc_build_system.bzl", "grpc_cc_test", "grpc_package")
licenses(["notice"])
grpc_package(name = "test/core/config")
grpc_cc_test(
name = "core_configuration_test",
srcs = ["core_configuration_test.cc"],
external_deps = ["gtest"],
language = "c++",
uses_polling = False,
deps = [
"//:config",
"//test/core/util:grpc_suppressions",
],
)

@ -0,0 +1,71 @@
// Copyright 2021 gRPC authors.
//
// 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.
#include "src/core/lib/config/core_configuration.h"
#include <gtest/gtest.h>
#include <chrono>
#include <thread>
namespace grpc_core {
// Allow substitution of config builder - in real code this would iterate
// through all plugins
namespace testing {
using ConfigBuilderFunction = std::function<void(CoreConfiguration::Builder*)>;
static ConfigBuilderFunction g_mock_builder;
} // namespace testing
void BuildCoreConfiguration(CoreConfiguration::Builder* builder) {
::grpc_core::testing::g_mock_builder(builder);
}
namespace testing {
// Helper for testing - clear out any state, rebuild configuration with fn being
// the initializer
void InitConfigWithBuilder(ConfigBuilderFunction fn) {
CoreConfiguration::Reset();
g_mock_builder = fn;
CoreConfiguration::Get();
g_mock_builder = nullptr;
}
TEST(ConfigTest, NoopConfig) {
InitConfigWithBuilder([](CoreConfiguration::Builder*) {});
CoreConfiguration::Get();
}
TEST(ConfigTest, ThreadedInit) {
CoreConfiguration::Reset();
g_mock_builder = [](CoreConfiguration::Builder*) {
std::this_thread::sleep_for(std::chrono::seconds(1));
};
std::vector<std::thread> threads;
threads.reserve(64);
for (int i = 0; i < 64; i++) {
threads.push_back(std::thread([]() { CoreConfiguration::Get(); }));
}
for (auto& t : threads) {
t.join();
}
g_mock_builder = nullptr;
CoreConfiguration::Get();
}
} // namespace testing
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -4369,6 +4369,30 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "core_configuration_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save