Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 194 additions & 36 deletions api/include/opentelemetry/context/context.h
Original file line number Diff line number Diff line change
@@ -1,61 +1,219 @@
#pragma once


#include <map>
#include <string>
#include <mutex>

#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace context
{

std::mutex context_id_mutex;

/*The context class provides a context identifier*/
class Context{

private:

/*The identifier itself*/
std::map<int, int> ctx_map_;

/*Used to track that last ContextKey identifier and create the next one */
static int last_key_identifier_;

/* Context: A constructor that accepts a key/value map*/
Context(std::map<int,int> ctx_map){
ctx_map_ = ctx_map;
}

public:

/*The ContextKey class is used to obscure access from the
* user to the context map. The identifier is used as a key
* to the context map.
*/
class ContextKey{
private:
friend class Context;

std::string key_name_;

int identifier_;


/* GetIdentifier: returns the identifier*/
int GetIdentifier(){
return identifier_;
}

/* ContextKey: constructs a new ContextKey with the
* passed in name and identifier.
*/
ContextKey(std::string key_name, int identifier){
key_name_ = key_name;
identifier_ = identifier;
}

public:

/* ContextKey: Consructs a new ContextKey with the passed in name
* and increments the identifier then assigns it to be the key's
* identifier.
*/
ContextKey(std::string key_name){
key_name_ = key_name;

context_id_mutex.lock();

Context::last_key_identifier_++;

identifier_ = Context::last_key_identifier_;

context_id_mutex.unlock();
}

};


/* Context: contructor, creates a context object with no key/value pairs
*/
Context(){
ctx_map_ = std::map<int,int> {};

}

/* Context: contructor, creates a context object from a map
* of keys and identifiers
*/
Context(ContextKey key, int value){
ctx_map_[key.GetIdentifier()] = value;
}


/* WriteValue: accepts a new key/value pair and then returns a new
* context that contains both the original pairs and the new pair.
*/
Context WriteValue(ContextKey key, int value){
std::map<int, int> temp_map = ctx_map_;

temp_map[key.GetIdentifier()] = value;

return Context(temp_map);
}


/* GetValue: Returns the value associated with the passed in key
*/
int GetValue(ContextKey key){
return ctx_map_[key.GetIdentifier()];
}

/* CreateKey: Returns a ContextKey that has the passed in name and the
* next available identifier.*/
ContextKey CreateKey(std::string key_name){
int id;

context_id_mutex.lock();

last_key_identifier_++;

id = last_key_identifier_;

context_id_mutex.unlock();

return ContextKey(key_name,id);
}


};



/* The token class provides an identifier that is used by
* the attach and detach methods to keep track of context
* objects.*/

class Token{
private:

Context ctx_;

public:

/* Token: A constructor that sets the token's Context object to the
* one that was passed in.
*/
Token(Context &ctx){
ctx_ = ctx;
}

/* GetContext: Returns the stored context object */
Context GetContext(){
return ctx_;
}

};


/* The RuntimeContext class provides a wrapper for
* propogating context through cpp*/
class RuntimeContext {
private:

static thread_local Context context_;

public:


/*The context class provides a context identifier*/
class Context{
private:
/*The identifier itself*/
std::map<std::string, int> ctx_;
RuntimeContext(){
context_ = Context();
}

public:
/* Context: contructor, creates a context object from a map
* of keys and identifiers
*/
Context(std::map<std::string, int> ctx);

};
/* RuntimeContext: A constructor that will set the context as
* the passed in context.
*/
RuntimeContext(Context &context){
context_ = context;
}

/* The token class provides an identifier that is used by
* the RuntimeContext attach and detach methods to keep track of context
* objects.*/
class Token{
Token();
};
/* attach: Sets the current 'Context' object. Returns a token
* that can be used to reset to the previous Context.
*/
Token Attach(Context &context){

Token old_context_token = Token(context_);

/* The RuntimeContext class provides a wrapper for
* propogating context through cpp*/
class RuntimeContext {
public:
context_ = context;

/* RuntimeContext: A constructor that will set the current
* context to the threading local.
*/
RuntimeContext();
return old_context_token;
}

/* attach: Sets the current 'Context' object. Returns a token
* that can be used to reset to the previous Context.
*/
static Token Attach(Context context);

/* GetCurrent: Return the current context.
*/
static Context GetCurrent(){
Context context = context_;
return context_;
}

/* get_current: Return the current context.
*/
static Context GetCurrent();


/* detach: Resets the context to a previous value.
*/
static void Detach(Token token);
/* Detach: Resets the context to a previous value stored in the
* passed in token.
*/
void Detach(Token &token){
context_ = token.GetContext();
}

};

thread_local Context RuntimeContext::context_ = Context();
int Context::last_key_identifier_ = 0;

};

}
OPENTELEMETRY_END_NAMESPACE
1 change: 1 addition & 0 deletions api/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
add_subdirectory(core)
add_subdirectory(context)
add_subdirectory(plugin)
add_subdirectory(nostd)
add_subdirectory(trace)
25 changes: 25 additions & 0 deletions api/test/context/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark")

cc_test(
name = "context_test",
srcs = [
"context_test.cc",
],
deps = [
"//api",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "runtimeContext_test",
srcs = [
"runtimeContext_test.cc",
],
copts = ["-Wall","-std=c++17"],
deps = [
"//api",
"@com_google_googletest//:gtest_main",
],
)

45 changes: 45 additions & 0 deletions api/test/context/context_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "opentelemetry/context/context.h"

#include <gtest/gtest.h>

using namespace opentelemetry::context;

/* Tests whether the original context objects changes
* when you write to it.
*/
TEST(Context_test, is_context_immutable)
{

Context test_context = Context();

Context::ContextKey test_key = test_context.CreateKey("test_key");

EXPECT_EQ(test_context.GetValue(test_key), NULL);

Context new_test_context = test_context.WriteValue(test_key,7);

EXPECT_EQ(new_test_context.GetValue(test_key), 7);

EXPECT_EQ(test_context.GetValue(test_key), NULL);
}

/* Tests whether the new Context Objects inherits the keys and values
* of the original context object
*/
TEST(Context_test, context_write_new_object)
{


Context::ContextKey test_key = Context::ContextKey("test_key");

Context test_context = Context(test_key, 7);

Context::ContextKey foo_key = test_context.CreateKey("foo_key");

Context new_test_context = test_context.WriteValue(foo_key,1);

EXPECT_EQ(new_test_context.GetValue(test_key), 7);

EXPECT_EQ(new_test_context.GetValue(foo_key), 1);
}

59 changes: 59 additions & 0 deletions api/test/context/runtimeContext_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include "opentelemetry/context/context.h"

#include <gtest/gtest.h>

using namespace opentelemetry::context;


/* Tests whether the runtimeContext object properly returns the current context
*/
TEST(runtimeContext_test, get_current_context)
{

Context::ContextKey test_key = Context::ContextKey("test_key");
Context test_context = Context(test_key, 7);

RuntimeContext test_runtime = RuntimeContext(test_context);

EXPECT_EQ(test_runtime.GetCurrent().GetValue(test_key),
test_context.GetValue(test_key));

}




/* Tests whether the runtimeContext object properly attaches and detaches
* the context object.
*/
TEST(runtimeContext_test, attach_detach_context)
{

Context::ContextKey test_key = Context::ContextKey("test_key");
Context test_context = Context(test_key, 7);

RuntimeContext test_runtime = RuntimeContext(test_context);

Context::ContextKey foo_key = Context::ContextKey("foo_key");
Context foo_context = Context(foo_key, 5);

EXPECT_EQ(test_runtime.GetCurrent().GetValue(test_key),
test_context.GetValue(test_key));
EXPECT_NE(test_runtime.GetCurrent().GetValue(foo_key),
foo_context.GetValue(foo_key));

Token test_token = test_runtime.Attach(foo_context);

EXPECT_NE(test_runtime.GetCurrent().GetValue(test_key),
test_context.GetValue(test_key));
EXPECT_EQ(test_runtime.GetCurrent().GetValue(foo_key),
foo_context.GetValue(foo_key));

test_runtime.Detach(test_token);

EXPECT_EQ(test_runtime.GetCurrent().GetValue(test_key),
test_context.GetValue(test_key));
EXPECT_NE(test_runtime.GetCurrent().GetValue(foo_key),
foo_context.GetValue(foo_key));
}