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

Commit

Permalink
Draft implementation of Context. (#200)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
g-easy committed Sep 27, 2018
1 parent e233b3f commit bd2b668
Show file tree
Hide file tree
Showing 9 changed files with 398 additions and 2 deletions.
63 changes: 63 additions & 0 deletions opencensus/context/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# OpenCensus C++ Context library.
# See context.h for details.
#
# Copyright 2018, OpenCensus 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("//opencensus:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")

licenses(["notice"]) # Apache 2.0

package(default_visibility = ["//visibility:private"])

cc_library(
name = "context",
srcs = [
"internal/context.cc",
"internal/with_context.cc",
],
hdrs = [
"context.h",
"with_context.h",
],
copts = DEFAULT_COPTS,
visibility = ["//visibility:public"],
deps = [
"//opencensus/tags",
"//opencensus/trace",
],
)

# Tests
# ========================================================================= #

cc_test(
name = "context_test",
srcs = ["internal/context_test.cc"],
copts = TEST_COPTS,
deps = [
":context",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "with_context_test",
srcs = ["internal/with_context_test.cc"],
copts = TEST_COPTS,
deps = [
":context",
"@com_google_googletest//:gtest_main",
],
)
69 changes: 69 additions & 0 deletions opencensus/context/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2018, OpenCensus 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 OPENCENSUS_CONTEXT_CONTEXT_H_
#define OPENCENSUS_CONTEXT_CONTEXT_H_

#include <functional>
#include <string>

#include "opencensus/tags/tag_map.h"
#include "opencensus/trace/span.h"

namespace opencensus {
namespace context {

// 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.
//
// This is a draft implementation of Context, and we chose to depend on TagMap
// and Span directly. In future, the implementation will change, so only rely
// on the public API for manipulating Contexts. In future we may support
// arbitrary keys and values.
class Context {
public:
// Returns a const reference to the current (thread local) Context.
static const Context& Current();

// Context is copiable and movable.
Context(const Context&) = default;
Context(Context&&) = default;
Context& operator=(const Context&) = default;
Context& operator=(Context&&) = default;

// Returns an std::function wrapped to run with a copy of this Context.
std::function<void()> Wrap(std::function<void()> fn) const;

// Returns a human-readable string for debugging. Do not rely on its format or
// try to parse it. Do not use the DebugString to retrieve Spans or Tags.
std::string DebugString() const;

private:
// Creates a default Context.
Context();

static Context* InternalMutableCurrent();
friend void swap(Context& a, Context& b);

friend class WithContext;

opencensus::tags::TagMap tags_;
opencensus::trace::Span span_;
};

} // namespace context
} // namespace opencensus

#endif // OPENCENSUS_CONTEXT_CONTEXT_H_
63 changes: 63 additions & 0 deletions opencensus/context/internal/context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2018, OpenCensus 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 "opencensus/context/context.h"

#include <functional>
#include <utility>

#include "absl/strings/str_cat.h"
#include "opencensus/context/with_context.h"
#include "opencensus/tags/tag_map.h"
#include "opencensus/trace/span.h"

namespace opencensus {
namespace context {

Context::Context()
: tags_(opencensus::tags::TagMap({})),
span_(opencensus::trace::Span::BlankSpan()) {}

// static
const Context& Context::Current() { return *InternalMutableCurrent(); }

std::function<void()> Context::Wrap(std::function<void()> fn) const {
Context copy(Context::Current());
return [fn, copy]() {
WithContext wc(copy);
fn();
};
}

std::string Context::DebugString() const {
return absl::StrCat("ctx@", absl::Hex(this),
" span=", span_.context().ToString(),
", tags=", tags_.DebugString());
}

// static
Context* Context::InternalMutableCurrent() {
static __thread Context* thread_ctx = nullptr;
if (thread_ctx == nullptr) thread_ctx = new Context;
return thread_ctx;
}

void swap(Context& a, Context& b) {
using std::swap;
swap(a.span_, b.span_);
swap(a.tags_, b.tags_);
}

} // namespace context
} // namespace opencensus
63 changes: 63 additions & 0 deletions opencensus/context/internal/context_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2018, OpenCensus 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 "opencensus/context/context.h"

#include <iostream>

#include "gtest/gtest.h"

// Not in namespace ::opencensus::context in order to better reflect what user
// code should look like.

namespace {

void LogCurrentContext() {
const std::string s = opencensus::context::Context::Current().DebugString();
std::cout << " current: " << s << "\n";
}

TEST(ContextTest, DefaultContext) { LogCurrentContext(); }

void Callback1() {
std::cout << " inside function\n";
LogCurrentContext();
}

TEST(ContextTest, Wrap) {
std::function<void()> fn =
opencensus::context::Context::Current().Wrap(Callback1);
fn();
}

TEST(ContextTest, WrapDoesNotLeak) {
{
std::function<void()> fn =
opencensus::context::Context::Current().Wrap(Callback1);
}
// We never call fn().
}

TEST(ContextTest, WrappedFnIsCopiable) {
std::function<void()> fn2;
{
std::function<void()> fn1 =
opencensus::context::Context::Current().Wrap(Callback1);
fn2 = fn1;
fn1();
}
fn2();
}

} // namespace
40 changes: 40 additions & 0 deletions opencensus/context/internal/with_context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2018, OpenCensus 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 "opencensus/context/with_context.h"

#include <utility>

#include "opencensus/context/context.h"

namespace opencensus {
namespace context {

WithContext::WithContext(const Context& ctx) : swapped_context_(ctx) {
using std::swap;
swap(*Context::InternalMutableCurrent(), swapped_context_);
}

WithContext::WithContext(Context&& ctx) : swapped_context_(std::move(ctx)) {
using std::swap;
swap(*Context::InternalMutableCurrent(), swapped_context_);
}

WithContext::~WithContext() {
using std::swap;
swap(*Context::InternalMutableCurrent(), swapped_context_);
}

} // namespace context
} // namespace opencensus
37 changes: 37 additions & 0 deletions opencensus/context/internal/with_context_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2018, OpenCensus 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 "opencensus/context/with_context.h"

#include <iostream>

#include "gtest/gtest.h"
#include "opencensus/context/context.h"

// Not in namespace ::opencensus::context in order to better reflect what user
// code should look like.

namespace {

TEST(WithContextTest, WithContext) {
opencensus::context::Context ctx = opencensus::context::Context::Current();
opencensus::context::WithContext wc(ctx);
}

TEST(WithContextTest, WithContextMovable) {
opencensus::context::Context ctx = opencensus::context::Context::Current();
opencensus::context::WithContext wc(std::move(ctx));
}

} // namespace
48 changes: 48 additions & 0 deletions opencensus/context/with_context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright 2018, OpenCensus 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 OPENCENSUS_CONTEXT_WITH_CONTEXT_H_
#define OPENCENSUS_CONTEXT_WITH_CONTEXT_H_

#include "opencensus/context/context.h"

namespace opencensus {
namespace context {

// WithContext is a scoped object that sets the current Context to the given
// one, until the WithContext object is destroyed.
//
// Because it changes the current (thread local) context, NEVER allocate a
// WithContext in one thread and deallocate in another. A simple way to ensure
// this is to only ever stack-allocate it.
class WithContext {
public:
explicit WithContext(const Context& ctx);
explicit WithContext(Context&& ctx);
~WithContext();

private:
WithContext() = delete;
WithContext(const WithContext&) = delete;
WithContext(WithContext&&) = delete;
WithContext& operator=(const WithContext&) = delete;
WithContext& operator=(WithContext&&) = delete;

Context swapped_context_;
};

} // namespace context
} // namespace opencensus

#endif // OPENCENSUS_CONTEXT_WITH_CONTEXT_H_
Loading

0 comments on commit bd2b668

Please sign in to comment.