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

Commit bd2b668

Browse files
authored
Draft implementation of Context. (#200)
Context holds information specific to an operation, such as a TagMap and Span. Each thread has a currently active Context. Contexts are conceptually immutable: the contents of a Context cannot be modified in-place.
1 parent e233b3f commit bd2b668

9 files changed

Lines changed: 398 additions & 2 deletions

File tree

opencensus/context/BUILD

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# OpenCensus C++ Context library.
2+
# See context.h for details.
3+
#
4+
# Copyright 2018, OpenCensus Authors
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
18+
load("//opencensus:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
19+
20+
licenses(["notice"]) # Apache 2.0
21+
22+
package(default_visibility = ["//visibility:private"])
23+
24+
cc_library(
25+
name = "context",
26+
srcs = [
27+
"internal/context.cc",
28+
"internal/with_context.cc",
29+
],
30+
hdrs = [
31+
"context.h",
32+
"with_context.h",
33+
],
34+
copts = DEFAULT_COPTS,
35+
visibility = ["//visibility:public"],
36+
deps = [
37+
"//opencensus/tags",
38+
"//opencensus/trace",
39+
],
40+
)
41+
42+
# Tests
43+
# ========================================================================= #
44+
45+
cc_test(
46+
name = "context_test",
47+
srcs = ["internal/context_test.cc"],
48+
copts = TEST_COPTS,
49+
deps = [
50+
":context",
51+
"@com_google_googletest//:gtest_main",
52+
],
53+
)
54+
55+
cc_test(
56+
name = "with_context_test",
57+
srcs = ["internal/with_context_test.cc"],
58+
copts = TEST_COPTS,
59+
deps = [
60+
":context",
61+
"@com_google_googletest//:gtest_main",
62+
],
63+
)

opencensus/context/context.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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+
#ifndef OPENCENSUS_CONTEXT_CONTEXT_H_
16+
#define OPENCENSUS_CONTEXT_CONTEXT_H_
17+
18+
#include <functional>
19+
#include <string>
20+
21+
#include "opencensus/tags/tag_map.h"
22+
#include "opencensus/trace/span.h"
23+
24+
namespace opencensus {
25+
namespace context {
26+
27+
// Context holds information specific to an operation, such as a TagMap and
28+
// Span. Each thread has a currently active Context. Contexts are conceptually
29+
// immutable: the contents of a Context cannot be modified in-place.
30+
//
31+
// This is a draft implementation of Context, and we chose to depend on TagMap
32+
// and Span directly. In future, the implementation will change, so only rely
33+
// on the public API for manipulating Contexts. In future we may support
34+
// arbitrary keys and values.
35+
class Context {
36+
public:
37+
// Returns a const reference to the current (thread local) Context.
38+
static const Context& Current();
39+
40+
// Context is copiable and movable.
41+
Context(const Context&) = default;
42+
Context(Context&&) = default;
43+
Context& operator=(const Context&) = default;
44+
Context& operator=(Context&&) = default;
45+
46+
// Returns an std::function wrapped to run with a copy of this Context.
47+
std::function<void()> Wrap(std::function<void()> fn) const;
48+
49+
// Returns a human-readable string for debugging. Do not rely on its format or
50+
// try to parse it. Do not use the DebugString to retrieve Spans or Tags.
51+
std::string DebugString() const;
52+
53+
private:
54+
// Creates a default Context.
55+
Context();
56+
57+
static Context* InternalMutableCurrent();
58+
friend void swap(Context& a, Context& b);
59+
60+
friend class WithContext;
61+
62+
opencensus::tags::TagMap tags_;
63+
opencensus::trace::Span span_;
64+
};
65+
66+
} // namespace context
67+
} // namespace opencensus
68+
69+
#endif // OPENCENSUS_CONTEXT_CONTEXT_H_
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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/context/context.h"
16+
17+
#include <functional>
18+
#include <utility>
19+
20+
#include "absl/strings/str_cat.h"
21+
#include "opencensus/context/with_context.h"
22+
#include "opencensus/tags/tag_map.h"
23+
#include "opencensus/trace/span.h"
24+
25+
namespace opencensus {
26+
namespace context {
27+
28+
Context::Context()
29+
: tags_(opencensus::tags::TagMap({})),
30+
span_(opencensus::trace::Span::BlankSpan()) {}
31+
32+
// static
33+
const Context& Context::Current() { return *InternalMutableCurrent(); }
34+
35+
std::function<void()> Context::Wrap(std::function<void()> fn) const {
36+
Context copy(Context::Current());
37+
return [fn, copy]() {
38+
WithContext wc(copy);
39+
fn();
40+
};
41+
}
42+
43+
std::string Context::DebugString() const {
44+
return absl::StrCat("ctx@", absl::Hex(this),
45+
" span=", span_.context().ToString(),
46+
", tags=", tags_.DebugString());
47+
}
48+
49+
// static
50+
Context* Context::InternalMutableCurrent() {
51+
static __thread Context* thread_ctx = nullptr;
52+
if (thread_ctx == nullptr) thread_ctx = new Context;
53+
return thread_ctx;
54+
}
55+
56+
void swap(Context& a, Context& b) {
57+
using std::swap;
58+
swap(a.span_, b.span_);
59+
swap(a.tags_, b.tags_);
60+
}
61+
62+
} // namespace context
63+
} // namespace opencensus
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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/context/context.h"
16+
17+
#include <iostream>
18+
19+
#include "gtest/gtest.h"
20+
21+
// Not in namespace ::opencensus::context in order to better reflect what user
22+
// code should look like.
23+
24+
namespace {
25+
26+
void LogCurrentContext() {
27+
const std::string s = opencensus::context::Context::Current().DebugString();
28+
std::cout << " current: " << s << "\n";
29+
}
30+
31+
TEST(ContextTest, DefaultContext) { LogCurrentContext(); }
32+
33+
void Callback1() {
34+
std::cout << " inside function\n";
35+
LogCurrentContext();
36+
}
37+
38+
TEST(ContextTest, Wrap) {
39+
std::function<void()> fn =
40+
opencensus::context::Context::Current().Wrap(Callback1);
41+
fn();
42+
}
43+
44+
TEST(ContextTest, WrapDoesNotLeak) {
45+
{
46+
std::function<void()> fn =
47+
opencensus::context::Context::Current().Wrap(Callback1);
48+
}
49+
// We never call fn().
50+
}
51+
52+
TEST(ContextTest, WrappedFnIsCopiable) {
53+
std::function<void()> fn2;
54+
{
55+
std::function<void()> fn1 =
56+
opencensus::context::Context::Current().Wrap(Callback1);
57+
fn2 = fn1;
58+
fn1();
59+
}
60+
fn2();
61+
}
62+
63+
} // namespace
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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/context/with_context.h"
16+
17+
#include <utility>
18+
19+
#include "opencensus/context/context.h"
20+
21+
namespace opencensus {
22+
namespace context {
23+
24+
WithContext::WithContext(const Context& ctx) : swapped_context_(ctx) {
25+
using std::swap;
26+
swap(*Context::InternalMutableCurrent(), swapped_context_);
27+
}
28+
29+
WithContext::WithContext(Context&& ctx) : swapped_context_(std::move(ctx)) {
30+
using std::swap;
31+
swap(*Context::InternalMutableCurrent(), swapped_context_);
32+
}
33+
34+
WithContext::~WithContext() {
35+
using std::swap;
36+
swap(*Context::InternalMutableCurrent(), swapped_context_);
37+
}
38+
39+
} // namespace context
40+
} // namespace opencensus
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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/context/with_context.h"
16+
17+
#include <iostream>
18+
19+
#include "gtest/gtest.h"
20+
#include "opencensus/context/context.h"
21+
22+
// Not in namespace ::opencensus::context in order to better reflect what user
23+
// code should look like.
24+
25+
namespace {
26+
27+
TEST(WithContextTest, WithContext) {
28+
opencensus::context::Context ctx = opencensus::context::Context::Current();
29+
opencensus::context::WithContext wc(ctx);
30+
}
31+
32+
TEST(WithContextTest, WithContextMovable) {
33+
opencensus::context::Context ctx = opencensus::context::Context::Current();
34+
opencensus::context::WithContext wc(std::move(ctx));
35+
}
36+
37+
} // namespace

opencensus/context/with_context.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
#ifndef OPENCENSUS_CONTEXT_WITH_CONTEXT_H_
16+
#define OPENCENSUS_CONTEXT_WITH_CONTEXT_H_
17+
18+
#include "opencensus/context/context.h"
19+
20+
namespace opencensus {
21+
namespace context {
22+
23+
// WithContext is a scoped object that sets the current Context to the given
24+
// one, until the WithContext object is destroyed.
25+
//
26+
// Because it changes the current (thread local) context, NEVER allocate a
27+
// WithContext in one thread and deallocate in another. A simple way to ensure
28+
// this is to only ever stack-allocate it.
29+
class WithContext {
30+
public:
31+
explicit WithContext(const Context& ctx);
32+
explicit WithContext(Context&& ctx);
33+
~WithContext();
34+
35+
private:
36+
WithContext() = delete;
37+
WithContext(const WithContext&) = delete;
38+
WithContext(WithContext&&) = delete;
39+
WithContext& operator=(const WithContext&) = delete;
40+
WithContext& operator=(WithContext&&) = delete;
41+
42+
Context swapped_context_;
43+
};
44+
45+
} // namespace context
46+
} // namespace opencensus
47+
48+
#endif // OPENCENSUS_CONTEXT_WITH_CONTEXT_H_

0 commit comments

Comments
 (0)