mirror of https://github.com/grpc/grpc.git
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
parent
f5d3ed2db1
commit
cad2255d8c
8 changed files with 314 additions and 0 deletions
@ -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(); |
||||
} |
Loading…
Reference in new issue