Context api alternative implementation #180
Conversation
…d added the contextvars_context.h file
Co-authored-by: Kirk Kelsey <kirkmkelsey@gmail.com>
…-cpp into context_api_content
| OPENTELEMETRY_BEGIN_NAMESPACE | ||
| namespace context | ||
| { | ||
| using ContextValue = nostd::variant<bool, int64_t, uint64_t, double>; |
There was a problem hiding this comment.
I think we'll need nostd::shared_ptr<SpanContext> too.
There was a problem hiding this comment.
When I tried to add it I got a: variant is implicitly deleted because the default definition would be ill-formed error
There was a problem hiding this comment.
So the nostd::shared_ptr does work. But whenever I try to add a unique_ptr of any type, I am getting the implicitly deleted because of ill formed definition error.
There was a problem hiding this comment.
nostd::shared_pr has been added is the unique_ptr necessary?
| // Creates a context object from a map of keys and identifiers, this will | ||
| // hold a shared_ptr to the head of the DataList linked list | ||
| template <class T, nostd::enable_if_t<trace::detail::is_key_value_iterable<T>::value> * = nullptr> | ||
| Context(const T &keys_and_values) |
There was a problem hiding this comment.
I'm not 100% sure, maybe I'm missing some part of the high-level view.
I think we'll use Context to obtain the currently active span: we will have a method to obtain the current context, the current context will hold the SpanContext of the active span. I'm trying to figure out necessary steps to make this possible:
- When a new span starts, we need to set a new current context. We will be able to get this new context via
SetValue("current_span", span_context). Then the new span will be stored in the new current context. - When the span ends, we will need to restore the previous context, as this should be the current context now (returning the parent span as active span).
I think we'll need some kind of parenting of contexts, but I'm not sure if it will be managed by the Context itself or by some other class.
| { | ||
| Context context = Context(values); | ||
| nostd::shared_ptr<DataList> last_node; | ||
| for (nostd::shared_ptr<DataList> data = context.head_; data != nullptr; data = data->next_) |
There was a problem hiding this comment.
Do we need shared_ptr while iterating through the linked list or there can be an optimization?
There was a problem hiding this comment.
Changed to just a ptr.
There was a problem hiding this comment.
This can be simplified a bit:
nostd::shared_ptr<DataList>& last = context.head_;
while (last->next_ != nullptr) { last = last->next_; }
last->next_ = head_; | } | ||
| } | ||
| } | ||
| return (int64_t)0; |
There was a problem hiding this comment.
Not blocking this PR, do we need a nil type?
There was a problem hiding this comment.
Boost has a boost::blank type for this purpose. The more C++ idiomatic way would be top wrap the return value in a std::optional, but if feel skeptical about extending nostd by this (it only was introduced in C++17).
I also think this shouldn't block this PR.
…-cpp into context_api_content
|
The change looks good enough to me. I think we might need some memory testing (e.g. checking if all the allocated linked-list nodes are freed up before exiting the application (e.g. by using our custom malloc/free) to make sure we're actually doing the correct/efficient ref-counting. Probably in another PR. Looks like there are still open questions/comments, I'll wait until we got approval from @pyohannes. |
pyohannes
left a comment
There was a problem hiding this comment.
A few more nits, but I think this is close to being done.
| key_ = new char[nostd::string_view(iter->first).size()]; | ||
| key_length_ = nostd::string_view(iter->first).size(); | ||
| memcpy(key_, nostd::string_view(iter->first).data(), | ||
| nostd::string_view(iter->first).size() * sizeof(char)); |
There was a problem hiding this comment.
Please leave out the explicit nostd::string_view here, and below. It makes the code easier to read.
| // and returns that head. | ||
| DataList(nostd::string_view key, ContextValue value) | ||
| { | ||
| key_ = new char[nostd::string_view(key).size()]; |
There was a problem hiding this comment.
No need for the explicit nostd::string_view here, this can just be key.size().
| // Builds a data list off of a key and value iterable and returns the head | ||
| template <class T, | ||
| nostd::enable_if_t<trace::detail::is_key_value_iterable<T>::value> * = nullptr> | ||
| DataList(const T &keys_and_vals) |
There was a problem hiding this comment.
Please add an additional move constructor:
DataList &operator=(DataList &&other)
{
key_length_ = other.key_length_;
value_ = std::move(other.value_);
next_ = std::move(other.next_);
key_ = other.key_;
other.key_ = nullptr;
return *this;
}Then you can simplify this constructor and avoid code duplication:
template <class T>
DataList(const T &keys_and_vals) : key_{nullptr}
{
bool first = true;
auto *node = this;
for (auto &iter : keys_and_vals)
{
if (first)
{
*node = std::move(DataList(iter.first, iter.second));
first = false;
}
else
{
node->next_ = nostd::shared_ptr<DataList>(new DataList(iter.first, iter.second));
node = node->next_.get();
}
}
} | } | ||
| } | ||
| } | ||
| return (int64_t)0; |
There was a problem hiding this comment.
Boost has a boost::blank type for this purpose. The more C++ idiomatic way would be top wrap the return value in a std::optional, but if feel skeptical about extending nostd by this (it only was introduced in C++17).
I also think this shouldn't block this PR.
| } | ||
|
|
||
| // Returns the value associated with the passed in key. | ||
| context::ContextValue GetValue(nostd::string_view key) |
There was a problem hiding this comment.
This can be const and noexcept.
| } | ||
|
|
||
| // Checks for key and returns true if found | ||
| bool HasKey(nostd::string_view key) |
There was a problem hiding this comment.
This can be const and noexcept.
| // Returns the value associated with the passed in key. | ||
| context::ContextValue GetValue(nostd::string_view key) | ||
| { | ||
| for (DataList *data = &*head_; data != nullptr; data = &*(data->next_)) |
There was a problem hiding this comment.
I think this is a bit more readable:
for (DataList *data = head_.get(); data != nullptr; data = data->next_.get())| { | ||
| Context context = Context(values); | ||
| nostd::shared_ptr<DataList> last_node; | ||
| for (nostd::shared_ptr<DataList> data = context.head_; data != nullptr; data = data->next_) |
There was a problem hiding this comment.
This can be simplified a bit:
nostd::shared_ptr<DataList>& last = context.head_;
while (last->next_ != nullptr) { last = last->next_; }
last->next_ = head_; | // Checks for key and returns true if found | ||
| bool HasKey(nostd::string_view key) | ||
| { | ||
| for (nostd::shared_ptr<DataList> data = head_; data != nullptr; data = data->next_) |
There was a problem hiding this comment.
We can avoid copies of shared_ptr here:
for (DataList *data = head_.get(); data != nullptr; data = data->next_.get())
I ran the unit test through valgrind, and it looks fine. It would be nice to have some automated memory checking though in our CI. |
|
Isn't there memory checking in the CI? |
pyohannes
left a comment
There was a problem hiding this comment.
Thanks for all you efforts on this PR. I have one minor nit with the include lists, otherwise this is ready to merge.
| #include "opentelemetry/context/context_value.h" | ||
| #include "opentelemetry/nostd/shared_ptr.h" | ||
| #include "opentelemetry/nostd/string_view.h" | ||
| #include "opentelemetry/trace/key_value_iterable_view.h" |
There was a problem hiding this comment.
Some include nits: I think atomic and key_value_iterable_view.h are not needed.
There was a problem hiding this comment.
I have removed them.
|
Please rebase and then @reyang please merge. |
Update dependency rules_go to v0.53.0
Links the context objects together as a linked list where each time new data is written to a context, a new node is added containing that new information and each node has access to all the data before it and none of the data after. Within each node there is another linked list actually containing the key value pairs. This implementation uses no STL and therefore I believe should have no ABI compatibility issues. @reyang @maxgolov.