diff --git a/api/include/opentelemetry/context/TBD b/api/include/opentelemetry/context/TBD deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/api/include/opentelemetry/context/context.h b/api/include/opentelemetry/context/context.h index ddb6227095..30c650cdea 100644 --- a/api/include/opentelemetry/context/context.h +++ b/api/include/opentelemetry/context/context.h @@ -1,233 +1,168 @@ #pragma once +#include +#include #include -#include -#include - +#include +#include + +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/utility.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/key_value_iterable.h" +#include "opentelemetry/trace/key_value_iterable_view.h" +#include "opentelemetry/trace/tracer.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace context { - std::mutex context_id_mutex; - - /*The context class provides a context identifier */ - class Context{ - - 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_; - } - - /* 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: - - /* 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(); - } - - }; - - - /* Creates a context object with no key/value pairs */ - Context(){ - ctx_map_ = std::map {}; - - } - - /* Contructor, creates a context object from a map - * of keys and identifiers - */ - Context(ContextKey key, int value){ - ctx_map_[key.GetIdentifier()] = value; - } - +/* The context class provides a context identifier */ +template +class Context +{ - /* 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 temp_map = ctx_map_; +public: + /*The Key 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 Key + { - temp_map[key.GetIdentifier()] = value; + public: + /*Returns the key's identifier*/ + nostd::string_view GetIdentifier() { return nostd::string_view(identifier_); } + /*Returns the key's name */ + nostd::string_view GetName() { return key_name_; } - return Context(temp_map); - } + private: + friend class Context; - /* Class comparator to see if the context maps are the same. */ - bool operator == (const Context &context){ - if(context.ctx_map_ == ctx_map_){ - return true; - } - else{ - return false; - } - } + /* Constructs a new Key with the passed in name. Sets the identifier as + * the address of this object. */ + Key(nostd::string_view key_name) : key_name_{key_name} + { + std::stringstream ss; + ss << (void *)this; + nostd::string_view temp_view; + temp_view = ss.str(); - /* Returns the value associated with the passed in key */ - int GetValue(ContextKey key){ - return ctx_map_[key.GetIdentifier()]; - } - - /* 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); - } - - private: - - /* The identifier itself */ - std::map ctx_map_; - - /*Used to track that last ContextKey identifier and create the next one */ - static int last_key_identifier_; - - /* A constructor that accepts a key/value map */ - Context(std::map ctx_map){ - ctx_map_ = ctx_map; - } + memcpy(identifier_, temp_view.data(), temp_view.size()); + } + nostd::string_view key_name_; + char identifier_[50]; }; + /* Creates a key with the passed in name and returns it. */ + Key CreateKey(nostd::string_view key_name) { return Key(key_name); } - - /* The token class provides an identifier that is used by - * the attach and detach methods to keep track of context - * objects. + /* Contructor, creates a context object from a map of keys + * and identifiers. */ + Context(const T &attributes) : key_vals_(attributes) + { + /* Currently only used as a check, to ensure T is of the right type. */ + trace::KeyValueIterableView iterable = trace::KeyValueIterableView(attributes); + } - class Token{ + Context() {} - public: - - /* A constructor that sets the token's Context object to the - * one that was passed in. - */ - Token(Context &ctx){ - ctx_ = ctx; - } - - /* Returns the stored context object */ - Context GetContext(){ - return ctx_; + /* Accepts a new iterable and then returns a new context that + * contains both the original pairs and the new pair. + */ + Context WriteValues(T &attributes) noexcept + { + /* Currently only used as a check, to ensure T is of the right type. */ + trace::KeyValueIterableView iterable = trace::KeyValueIterableView(attributes); + + std::insert_iterator back(attributes, std::begin(attributes)); + + for (auto iter = std::begin(key_vals_); iter != std::end(key_vals_); ++iter) + { + back = *iter; + } + + return Context(attributes); + } + + /* Returns the value associated with the passed in key */ + common::AttributeValue GetValue(Key key) + { + + for (auto iter = std::begin(key_vals_); iter != std::end(key_vals_); ++iter) + { + if (key.GetIdentifier() == iter->first) + { + return iter->second; } - - private: - - Context ctx_; - }; - - - /* The RuntimeContext class provides a wrapper for - * propogating context through cpp. */ - class RuntimeContext { - - public: - - - /* A default constructor that will set the context to - * an empty context object. - */ - RuntimeContext(){ - context_ = Context(); + } + + return ""; + } + + /* Iterates through the current and comparing context objects + * to check for equality, */ + bool operator==(const Context &other) + { + + /*Check for case where either both or one object has no contents. */ + if (std::begin(other.key_vals_) == std::end(other.key_vals_)) + { + if (std::begin(key_vals_) == std::end(key_vals_)) + { + return true; } - - /* A constructor that will set the context as the passed in context. */ - RuntimeContext(Context &context){ - context_ = context; + else + { + return false; } - - /* 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_); - - context_ = context; - - return old_context_token; - } - - - /* Return the current context. */ - static Context GetCurrent(){ - Context context = context_; - return context_; - } - - - /* Resets the context to a previous value stored in the - * passed in token. Returns zero if successful, -1 otherwise - */ - int Detach(Token &token){ - - if(token.GetContext() == context_){ - - return -1; + } + + if (std::begin(key_vals_) == std::end(key_vals_)) + { + return false; + } + + /*Compare the contexts*/ + for (auto iter = std::begin(key_vals_); iter != std::end(key_vals_); ++iter) + { + int found = 0; + + for (auto iter_other = std::begin(other.key_vals_); iter_other != std::end(other.key_vals_); + iter_other++) + { + if (iter->first == iter_other->first) + { + if (nostd::get(iter->second) == + nostd::get(iter_other->second)) + { + found = 1; + break; + } } - - context_ = token.GetContext(); - - return 0; } + if (found == 0) + { + return false; + } + } - private: - - static thread_local Context context_; + return true; + } - }; - - thread_local Context RuntimeContext::context_ = Context(); - int Context::last_key_identifier_ = 0; + /* Copy Constructors. */ + Context(const Context &other) = default; + Context &operator=(const Context &other) = default; +private: + T key_vals_; +}; -} +} // namespace context OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/runtime_context.h b/api/include/opentelemetry/context/runtime_context.h new file mode 100644 index 0000000000..b70f4ead1e --- /dev/null +++ b/api/include/opentelemetry/context/runtime_context.h @@ -0,0 +1,50 @@ +#pragma once + +#include "context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace context +{ + +/* The RuntimeContext class provides a wrapper for + * propogating context through cpp. */ +template +class RuntimeContext +{ +public: + /* The token class provides an identifier that is used by + * the attach and detach methods to keep track of context + * objects. + */ + class Token + { + private: + friend class RuntimeContext; + + Context context_; + + /* A constructor that sets the token's Context object to the + * one that was passed in. + */ + Token(Context &context) { context_ = context; } + + /* Returns the stored context object. */ + Context GetContext() { return context_; } + }; + + /* Return the current context. */ + Context GetCurrent(); + + /* Sets the current 'Context' object. Returns a token + * that can be used to reset to the previous Context. + */ + + Token Attach(Context &context); + + /* Resets the context to a previous value stored in the + * passed in token. Returns zero if successful, -1 otherwise + */ + int Detach(Token &token); +}; +} // namespace context +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/context/threadlocal_context.h b/api/include/opentelemetry/context/threadlocal_context.h new file mode 100644 index 0000000000..7ecf2b5709 --- /dev/null +++ b/api/include/opentelemetry/context/threadlocal_context.h @@ -0,0 +1,73 @@ +#pragma once + +#include "opentelemetry/context/runtime_context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace context +{ +/* The ThreadLocalContext class is a derived class from RuntimeContext and + * provides a wrapper for + * propogating context through cpp thread locally. */ +template +class ThreadLocalContext : public RuntimeContext +{ +public: + /* The token class provides an identifier that is used by + * the attach and detach methods to keep track of context + * objects. + */ + class Token + { + private: + friend class ThreadLocalContext; + + Context context_; + + /* A constructor that sets the token's Context object to the + * one that was passed in. + */ + Token(Context &context) { context_ = context; } + + /* Returns the stored context object. */ + Context GetContext() { return context_; } + }; + + /* Return the current context. */ + static Context GetCurrent() { return GetInstance(); } + + /* Resets the context to a previous value stored in the + * passed in token. Returns zero if successful, -1 otherwise + */ + static int Detach(Token &token) + { + + /* If the token context is the current context, return failure. */ + if (token.GetContext() == GetCurrent()) + { + return 1; + } + + GetInstance() = token.GetContext(); + return 0; + } + + /* Sets the current 'Context' object. Returns a token + * that can be used to reset to the previous Context. + */ + static Token Attach(Context &context) + { + Token old_context = Token(GetInstance()); + GetInstance() = context; + return old_context; + } + +private: + /* Provides storage and access to the thread_local context object */ + static Context &GetInstance() + { + static thread_local Context instance; + return instance; + } +}; +} // namespace context +OPENTELEMETRY_END_NAMESPACE diff --git a/api/test/context/BUILD b/api/test/context/BUILD index f69f02509c..68363db1c9 100644 --- a/api/test/context/BUILD +++ b/api/test/context/BUILD @@ -12,14 +12,12 @@ cc_test( ) cc_test( - name = "runtimeContext_test", + name = "thread_local_test", srcs = [ - "runtimeContext_test.cc", + "thread_local_test.cc", ], - copts = ["-Wall","-std=c++17"], deps = [ "//api", "@com_google_googletest//:gtest_main", ], ) - diff --git a/api/test/context/context_test.cc b/api/test/context/context_test.cc index a8903be443..50c8635553 100644 --- a/api/test/context/context_test.cc +++ b/api/test/context/context_test.cc @@ -1,45 +1,125 @@ +#include + #include "opentelemetry/context/context.h" #include -using namespace opentelemetry::context; +using namespace opentelemetry; + +/* Test ensurs that the context object doe not change when you write to it */ +TEST(ContextTest, ContextImmutability) +{ + using M = std::map; + context::Context test_context = context::Context(); + + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("test_key"); -/* Tests whether the original context objects changes - * when you write to it. + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + + context::Context foo_context = test_context.WriteValues(m1); + + EXPECT_NE(nostd::get(test_context.GetValue(test_key)), "123"); + EXPECT_NE(nostd::get(test_context.GetValue(foo_key)), "456"); +} + +/* Tests whether the new Context Objects inherits the keys and values + * of the original context object. */ -TEST(Context_test, is_context_immutable) +TEST(ContextTest, ContextInheritance) { - Context test_context = Context(); + using M = std::map; + context::Context test_context = context::Context(); - Context::ContextKey test_key = test_context.CreateKey("test_key"); - - EXPECT_EQ(test_context.GetValue(test_key), NULL); + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("foo_key"); + context::Context::Key other_key = test_context.CreateKey("other_key"); - Context new_test_context = test_context.WriteValue(test_key,7); + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + M m2 = {{std::string(other_key.GetIdentifier()), "000"}}; - EXPECT_EQ(new_test_context.GetValue(test_key), 7); + context::Context foo_context = test_context.WriteValues(m1); + context::Context other_context = foo_context.WriteValues(m2); - EXPECT_EQ(test_context.GetValue(test_key), NULL); + EXPECT_EQ(nostd::get(other_context.GetValue(test_key)), "123"); + EXPECT_EQ(nostd::get(other_context.GetValue(foo_key)), "456"); } -/* Tests whether the new Context Objects inherits the keys and values - * of the original context object +/* Tests that when you add a key value pair where the key is already in + * existance, they key is overwritten. */ -TEST(Context_test, context_write_new_object) +TEST(ContextTest, ContextKeyOverwrite) +{ + + using M = std::map; + context::Context test_context = context::Context(); + + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("foo_key"); + context::Context::Key other_key = test_context.CreateKey("other_key"); + + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + M m2 = {{std::string(test_key.GetIdentifier()), "000"}}; + + context::Context foo_context = test_context.WriteValues(m1); + context::Context other_context = foo_context.WriteValues(m2); + + EXPECT_EQ(nostd::get(other_context.GetValue(test_key)), "000"); + EXPECT_NE(nostd::get(other_context.GetValue(test_key)), "123"); +} + +/* Tests that copying a context copies the key value pairs properly. */ +TEST(ContextTest, ContextCopy) { + using M = std::map; + context::Context test_context = context::Context(); - Context::ContextKey test_key = Context::ContextKey("test_key"); + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("foo_key"); + context::Context::Key other_key = test_context.CreateKey("other_key"); - Context test_context = Context(test_key, 7); - - Context::ContextKey foo_key = test_context.CreateKey("foo_key"); + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + M m2 = {{std::string(other_key.GetIdentifier()), "000"}}; - Context new_test_context = test_context.WriteValue(foo_key,1); - - EXPECT_EQ(new_test_context.GetValue(test_key), 7); + context::Context foo_context = test_context.WriteValues(m1); + context::Context other_context = foo_context.WriteValues(m2); + context::Context copied_context = other_context; - EXPECT_EQ(new_test_context.GetValue(foo_key), 1); + EXPECT_EQ(nostd::get(copied_context.GetValue(test_key)), "123"); + EXPECT_EQ(nostd::get(copied_context.GetValue(foo_key)), "456"); } +/* Tests that the comparison compares properly. */ +TEST(ContextTest, ContextCompare) +{ + + using M = std::map; + context::Context test_context = context::Context(); + + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("foo_key"); + context::Context::Key other_key = test_context.CreateKey("other_key"); + + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + M m2 = {{std::string(other_key.GetIdentifier()), "000"}}; + + context::Context foo_context = test_context.WriteValues(m1); + context::Context other_context = foo_context.WriteValues(m2); + context::Context copied_context = other_context; + + if (copied_context == other_context) + { + } + + if (copied_context == foo_context) + { + ADD_FAILURE(); + } +} diff --git a/api/test/context/runtimeContext_test.cc b/api/test/context/runtimeContext_test.cc deleted file mode 100644 index e283071c1d..0000000000 --- a/api/test/context/runtimeContext_test.cc +++ /dev/null @@ -1,61 +0,0 @@ -#include "opentelemetry/context/context.h" - -#include - -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)); - - int detach_result = test_runtime.Detach(test_token); - - EXPECT_EQ(detach_result, 0); - - 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)); -} - diff --git a/api/test/context/thread_local_test.cc b/api/test/context/thread_local_test.cc new file mode 100644 index 0000000000..8f20b75f2a --- /dev/null +++ b/api/test/context/thread_local_test.cc @@ -0,0 +1,34 @@ + +#include "opentelemetry/context/threadlocal_context.h" + +#include + +using namespace opentelemetry; + +/* Tests whether the ThreadLocalContext attaches and detaches as expected */ +TEST(ThreadLocalContextTest, ThreadLocalContextAttachDetach) +{ + + using M = std::map; + + context::Context test_context = context::Context(); + context::Context::Key test_key = test_context.CreateKey("test_key"); + context::Context::Key foo_key = test_context.CreateKey("test_key"); + + M m1 = {{std::string(test_key.GetIdentifier()), "123"}, + {std::string(foo_key.GetIdentifier()), "456"}}; + + context::Context foo_context = test_context.WriteValues(m1); + + context::ThreadLocalContext test_thread_context; + + context::ThreadLocalContext::Token test_token = test_thread_context.Attach(foo_context); + + EXPECT_EQ(nostd::get(test_thread_context.GetCurrent().GetValue(test_key)), + "123"); + + EXPECT_EQ(test_thread_context.Detach(test_token), 0); + + EXPECT_NE(nostd::get(test_thread_context.GetCurrent().GetValue(test_key)), + "123"); +}