Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit 791e19b

Browse files
g-easyBogdan Drutu
authored andcommitted
WithTagMap: puts a TagMap into the current Context. (#209)
* Add withTagMap. * Add test. * Format. * Test Rvalue constructor. * Add benchmark.
1 parent 1d202d3 commit 791e19b

6 files changed

Lines changed: 398 additions & 0 deletions

File tree

opencensus/context/context.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "opencensus/trace/span.h"
2323

2424
namespace opencensus {
25+
namespace tags {
26+
class WithTagMap;
27+
} // namespace tags
2528
namespace trace {
2629
class WithSpan;
2730
} // namespace trace
@@ -62,6 +65,7 @@ class Context {
6265

6366
friend class ContextTestPeer;
6467
friend class WithContext;
68+
friend class ::opencensus::tags::WithTagMap;
6569
friend class ::opencensus::trace::WithSpan;
6670

6771
opencensus::tags::TagMap tags_;

opencensus/tags/BUILD

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ cc_library(
4141
],
4242
)
4343

44+
cc_library(
45+
name = "with_tag_map",
46+
srcs = ["internal/with_tag_map.cc"],
47+
hdrs = ["with_tag_map.h"],
48+
copts = DEFAULT_COPTS,
49+
visibility = ["//visibility:public"],
50+
deps = [
51+
":tags",
52+
"//opencensus/context",
53+
],
54+
)
55+
4456
# Tests
4557
# ========================================================================= #
4658

@@ -63,3 +75,32 @@ cc_test(
6375
"@com_google_googletest//:gtest_main",
6476
],
6577
)
78+
79+
cc_test(
80+
name = "with_tag_map_test",
81+
srcs = ["internal/with_tag_map_test.cc"],
82+
copts = TEST_COPTS,
83+
deps = [
84+
":tags",
85+
":with_tag_map",
86+
"//opencensus/context",
87+
"@com_google_googletest//:gtest_main",
88+
],
89+
)
90+
91+
# Benchmarks
92+
# ========================================================================= #
93+
94+
cc_binary(
95+
name = "with_tag_map_benchmark",
96+
testonly = 1,
97+
srcs = ["internal/with_tag_map_benchmark.cc"],
98+
copts = TEST_COPTS,
99+
linkopts = ["-pthread"], # Required for absl/synchronization bits.
100+
linkstatic = 1,
101+
deps = [
102+
":tags",
103+
":with_tag_map",
104+
"@com_github_google_benchmark//:benchmark",
105+
],
106+
)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/tags/with_tag_map.h"
16+
17+
#include <cassert>
18+
#include <utility>
19+
20+
#include "opencensus/context/context.h"
21+
#include "opencensus/tags/tag_map.h"
22+
23+
using ::opencensus::context::Context;
24+
using ::opencensus::tags::TagMap;
25+
26+
namespace opencensus {
27+
namespace tags {
28+
29+
WithTagMap::WithTagMap(const TagMap& tags, bool cond)
30+
: swapped_tags_(tags)
31+
#ifndef NDEBUG
32+
,
33+
original_context_(Context::InternalMutableCurrent())
34+
#endif
35+
,
36+
cond_(cond) {
37+
ConditionalSwap();
38+
}
39+
40+
WithTagMap::WithTagMap(TagMap&& tags, bool cond)
41+
: swapped_tags_(std::move(tags))
42+
#ifndef NDEBUG
43+
,
44+
original_context_(Context::InternalMutableCurrent())
45+
#endif
46+
,
47+
cond_(cond) {
48+
ConditionalSwap();
49+
}
50+
51+
WithTagMap::~WithTagMap() {
52+
#ifndef NDEBUG
53+
assert(original_context_ == Context::InternalMutableCurrent() &&
54+
"WithTagMap must be destructed on the same thread as it was "
55+
"constructed.");
56+
#endif
57+
ConditionalSwap();
58+
}
59+
60+
void WithTagMap::ConditionalSwap() {
61+
if (cond_) {
62+
using std::swap;
63+
swap(Context::InternalMutableCurrent()->tags_, swapped_tags_);
64+
}
65+
}
66+
67+
} // namespace tags
68+
} // namespace opencensus
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/tags/with_tag_map.h"
16+
17+
#include <cstdlib>
18+
19+
#include "benchmark/benchmark.h"
20+
#include "opencensus/tags/tag_key.h"
21+
#include "opencensus/tags/tag_map.h"
22+
23+
namespace opencensus {
24+
namespace tags {
25+
namespace {
26+
27+
// Returns an example TagMap.
28+
TagMap Tags() {
29+
static const auto k1 = TagKey::Register("key1");
30+
static const auto k2 = TagKey::Register("key1");
31+
static const auto k3 = TagKey::Register("key1");
32+
return TagMap({{k1, "val1"}, {k2, "val2"}, {k3, "val3"}});
33+
}
34+
35+
void BM_WithTagMapCopy(benchmark::State& state) {
36+
const auto tags = Tags();
37+
for (auto _ : state) {
38+
WithTagMap wt(tags);
39+
}
40+
}
41+
BENCHMARK(BM_WithTagMapCopy);
42+
43+
void BM_WithTagMapCopyConditionFalse(benchmark::State& state) {
44+
const auto tags = Tags();
45+
for (auto _ : state) {
46+
WithTagMap wt(tags, false);
47+
}
48+
}
49+
BENCHMARK(BM_WithTagMapCopyConditionFalse);
50+
51+
void BM_TagMapConstruct(benchmark::State& state) {
52+
for (auto _ : state) {
53+
auto tags = Tags();
54+
benchmark::DoNotOptimize(tags);
55+
}
56+
}
57+
BENCHMARK(BM_TagMapConstruct);
58+
59+
void BM_WithTagMapConstructAndCopy(benchmark::State& state) {
60+
for (auto _ : state) {
61+
const auto tags = Tags();
62+
WithTagMap wt(tags);
63+
}
64+
}
65+
BENCHMARK(BM_WithTagMapConstructAndCopy);
66+
67+
void BM_WithTagMapConstructAndMove(benchmark::State& state) {
68+
for (auto _ : state) {
69+
auto tags = Tags();
70+
WithTagMap wt(std::move(tags));
71+
}
72+
}
73+
BENCHMARK(BM_WithTagMapConstructAndMove);
74+
75+
} // namespace
76+
} // namespace tags
77+
} // namespace opencensus
78+
79+
BENCHMARK_MAIN();
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/tags/with_tag_map.h"
16+
17+
#include <iostream>
18+
#include <thread>
19+
20+
#include "gtest/gtest.h"
21+
#include "opencensus/context/context.h"
22+
#include "opencensus/tags/tag_key.h"
23+
#include "opencensus/tags/tag_map.h"
24+
25+
namespace opencensus {
26+
namespace context {
27+
class ContextTestPeer {
28+
public:
29+
static const opencensus::tags::TagMap& CurrentTags() {
30+
return Context::InternalMutableCurrent()->tags_;
31+
}
32+
};
33+
} // namespace context
34+
} // namespace opencensus
35+
36+
// Not in namespace ::opencensus::context in order to better reflect what user
37+
// code should look like.
38+
39+
namespace {
40+
41+
using opencensus::context::ContextTestPeer;
42+
43+
void LogCurrentContext() {
44+
const std::string s = opencensus::context::Context::Current().DebugString();
45+
std::cout << " current: " << s << "\n";
46+
}
47+
48+
void ExpectNoTags() {
49+
EXPECT_EQ(opencensus::tags::TagMap({}), ContextTestPeer::CurrentTags());
50+
}
51+
52+
// Returns an example TagMap for testing.
53+
opencensus::tags::TagMap Tags1() {
54+
static const auto k1 = opencensus::tags::TagKey::Register("key1");
55+
static const auto k2 = opencensus::tags::TagKey::Register("key2");
56+
return opencensus::tags::TagMap({{k1, "val1"}, {k2, "val2"}});
57+
}
58+
59+
// Returns a different TagMap for testing.
60+
opencensus::tags::TagMap Tags2() {
61+
static const auto k3 = opencensus::tags::TagKey::Register("key3");
62+
return opencensus::tags::TagMap({{k3, "val3"}});
63+
}
64+
65+
TEST(WithTagsTest, NoTagsInDefaultContext) { ExpectNoTags(); }
66+
67+
TEST(WithTagsTest, TagsAreAddedAndRemoved) {
68+
const auto tags = Tags1();
69+
ExpectNoTags();
70+
{
71+
opencensus::tags::WithTagMap wt(tags);
72+
LogCurrentContext();
73+
EXPECT_EQ(tags, ContextTestPeer::CurrentTags()) << "Tags were installed.";
74+
}
75+
ExpectNoTags();
76+
}
77+
78+
TEST(WithTagsTest, RvalueConstructor) {
79+
auto tags = Tags1();
80+
ExpectNoTags();
81+
{
82+
opencensus::tags::WithTagMap wt(std::move(tags));
83+
EXPECT_EQ(Tags1(), ContextTestPeer::CurrentTags())
84+
<< "Tags were installed.";
85+
}
86+
ExpectNoTags();
87+
}
88+
89+
TEST(WithTagsTest, Nesting) {
90+
const auto tags1 = Tags1();
91+
const auto tags2 = Tags2();
92+
93+
ExpectNoTags();
94+
{
95+
opencensus::tags::WithTagMap wt1(tags1);
96+
EXPECT_EQ(tags1, ContextTestPeer::CurrentTags()) << "Tags1 installed.";
97+
{
98+
opencensus::tags::WithTagMap wt2(tags2);
99+
EXPECT_EQ(tags2, ContextTestPeer::CurrentTags()) << "Tags2 installed.";
100+
}
101+
EXPECT_EQ(tags1, ContextTestPeer::CurrentTags())
102+
<< "Tags2 uninstalled, back to Tags1.";
103+
}
104+
ExpectNoTags();
105+
}
106+
107+
TEST(WithTagsTest, DisabledViaConditional) {
108+
const auto tags1 = Tags1();
109+
const auto tags2 = Tags2();
110+
111+
ExpectNoTags();
112+
{
113+
opencensus::tags::WithTagMap wt1(tags1);
114+
EXPECT_EQ(tags1, ContextTestPeer::CurrentTags()) << "Tags1 installed.";
115+
{
116+
opencensus::tags::WithTagMap wt2(tags2, false);
117+
EXPECT_EQ(tags1, ContextTestPeer::CurrentTags())
118+
<< "Tags2 was NOT installed, blocked by condition.";
119+
}
120+
EXPECT_EQ(tags1, ContextTestPeer::CurrentTags())
121+
<< "Still Tags1 after WithTags2 goes out of scope.";
122+
}
123+
ExpectNoTags();
124+
}
125+
126+
#ifndef NDEBUG
127+
TEST(WithTagsDeathTest, DestructorOnWrongThread) {
128+
const auto tags = Tags1();
129+
130+
EXPECT_DEATH_IF_SUPPORTED(
131+
{
132+
auto* wt = new opencensus::tags::WithTagMap(tags);
133+
std::thread t([wt]() {
134+
// Running the destructor in a different thread corrupts its
135+
// thread-local Context. In debug mode, it assert()s.
136+
delete wt;
137+
});
138+
t.join();
139+
},
140+
"must be destructed on the same thread");
141+
}
142+
#endif
143+
144+
} // namespace

0 commit comments

Comments
 (0)