Skip to content

Commit 279be29

Browse files
Global Context Manager (#52)
* feat: add global context manager Signed-off-by: NeaguGeorgiana23 <neagugeorgiana@google.com> * Change Bazel version Signed-off-by: NeaguGeorgiana23 <neagugeorgiana@google.com> --------- Signed-off-by: NeaguGeorgiana23 <neagugeorgiana@google.com>
1 parent 2243d85 commit 279be29

6 files changed

Lines changed: 170 additions & 0 deletions

File tree

.bazelversion

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
8.5.1

openfeature/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
load("@rules_cc//cc:defs.bzl", "cc_library")
2+
13
package(
24
default_visibility = ["//visibility:public"],
35
)
@@ -55,6 +57,16 @@ cc_library(
5557
include_prefix = "openfeature",
5658
)
5759

60+
cc_library(
61+
name = "global_context_manager",
62+
srcs = ["global_context_manager.cpp"],
63+
hdrs = ["global_context_manager.h"],
64+
include_prefix = "openfeature",
65+
deps = [
66+
":evaluation_context",
67+
],
68+
)
69+
5870
cc_library(
5971
name = "metadata",
6072
hdrs = ["metadata.h"],
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "openfeature/global_context_manager.h"
2+
3+
#include <mutex>
4+
5+
namespace openfeature {
6+
7+
GlobalContextManager& GlobalContextManager::GetInstance() {
8+
static GlobalContextManager instance;
9+
return instance;
10+
}
11+
12+
void GlobalContextManager::SetGlobalEvaluationContext(
13+
const EvaluationContext& ctx) {
14+
std::unique_lock<std::shared_mutex> lock(context_mutex_);
15+
global_evaluation_context_ = ctx;
16+
}
17+
18+
EvaluationContext GlobalContextManager::GetGlobalEvaluationContext() const {
19+
std::shared_lock<std::shared_mutex> lock(context_mutex_);
20+
return global_evaluation_context_;
21+
}
22+
23+
} // namespace openfeature
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef CPP_SDK_INCLUDE_OPENFEATURE_GLOBAL_CONTEXT_MANAGER_H_
2+
#define CPP_SDK_INCLUDE_OPENFEATURE_GLOBAL_CONTEXT_MANAGER_H_
3+
4+
#include <memory>
5+
#include <shared_mutex>
6+
7+
#include "openfeature/evaluation_context.h"
8+
9+
namespace openfeature {
10+
11+
// Manages the global Evaluation Context for the OpenFeature SDK.
12+
// This data is static across the application (unless explicitly changed)
13+
class GlobalContextManager {
14+
public:
15+
static GlobalContextManager& GetInstance();
16+
17+
GlobalContextManager(const GlobalContextManager&) = delete;
18+
GlobalContextManager& operator=(const GlobalContextManager&) = delete;
19+
20+
// Updates the global evaluation context.
21+
void SetGlobalEvaluationContext(const EvaluationContext& ctx);
22+
23+
// Retrieves the current global evaluation context.
24+
EvaluationContext GetGlobalEvaluationContext() const;
25+
26+
private:
27+
GlobalContextManager() = default;
28+
EvaluationContext global_evaluation_context_;
29+
mutable std::shared_mutex context_mutex_;
30+
};
31+
32+
} // namespace openfeature
33+
34+
#endif // CPP_SDK_INCLUDE_OPENFEATURE_GLOBAL_CONTEXT_MANAGER_H_

test/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
2+
13
cc_library(
24
name = "mock_feature_provider",
35
testonly = True,
@@ -46,3 +48,12 @@ cc_test(
4648
"@googletest//:gtest_main",
4749
],
4850
)
51+
52+
cc_test(
53+
name = "global_context_manager_test",
54+
srcs = ["global_context_manager_test.cpp"],
55+
deps = [
56+
"//openfeature:global_context_manager",
57+
"@googletest//:gtest_main",
58+
],
59+
)
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "openfeature/global_context_manager.h"
2+
3+
#include <gtest/gtest.h>
4+
5+
#include <atomic>
6+
#include <thread>
7+
#include <vector>
8+
9+
#include "openfeature/evaluation_context.h"
10+
11+
using namespace openfeature;
12+
13+
class GlobalContextManagerTest : public ::testing::Test {
14+
protected:
15+
// Reset to a clean state before every test.
16+
void SetUp() override {
17+
GlobalContextManager::GetInstance().SetGlobalEvaluationContext(
18+
EvaluationContext{});
19+
}
20+
};
21+
22+
TEST_F(GlobalContextManagerTest, ReturnsSameInstance) {
23+
GlobalContextManager& instance1 = GlobalContextManager::GetInstance();
24+
GlobalContextManager& instance2 = GlobalContextManager::GetInstance();
25+
26+
EXPECT_EQ(&instance1, &instance2);
27+
}
28+
29+
TEST_F(GlobalContextManagerTest, SetAndGetContext) {
30+
GlobalContextManager& manager = GlobalContextManager::GetInstance();
31+
EvaluationContext input_ctx;
32+
33+
EXPECT_NO_THROW(manager.SetGlobalEvaluationContext(input_ctx));
34+
35+
EvaluationContext output_ctx = manager.GetGlobalEvaluationContext();
36+
EXPECT_NO_THROW(output_ctx = manager.GetGlobalEvaluationContext());
37+
38+
// NOTE: Since EvaluationContext is currently an empty class,
39+
// we cannot assert EQ or check member values.
40+
// Currently, the test simply proves the API calls succeed and copy semantics
41+
// work.
42+
43+
// TODO: Add assertions.
44+
}
45+
46+
TEST_F(GlobalContextManagerTest, ThreadSafetyStressTest) {
47+
GlobalContextManager& manager = GlobalContextManager::GetInstance();
48+
std::atomic<bool> stop{false};
49+
50+
// Writer Thread: Continuously updates the context.
51+
std::thread writer([&]() {
52+
while (!stop) {
53+
EvaluationContext ctx;
54+
// In a real scenario, we would populate ctx with different data here.
55+
manager.SetGlobalEvaluationContext(ctx);
56+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
57+
}
58+
});
59+
60+
// Reader Threads: Continuously read the context.
61+
std::vector<std::thread> readers;
62+
for (int i = 0; i < 10; ++i) {
63+
readers.emplace_back([&]() {
64+
while (!stop) {
65+
// Just calling the getter to ensure locks work and no race conditions
66+
// occur resulting in a crash (segfault).
67+
EvaluationContext ctx = manager.GetGlobalEvaluationContext();
68+
69+
// Prevent optimization from removing the call.
70+
volatile size_t size = sizeof(ctx);
71+
(void)size;
72+
}
73+
});
74+
}
75+
76+
// Let the chaos run for a short duration.
77+
std::this_thread::sleep_for(std::chrono::milliseconds(100));
78+
79+
stop = true;
80+
writer.join();
81+
for (auto& t : readers) {
82+
t.join();
83+
}
84+
}
85+
86+
int main(int argc, char** argv) {
87+
testing::InitGoogleTest(&argc, argv);
88+
return RUN_ALL_TESTS();
89+
}

0 commit comments

Comments
 (0)