Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime and threadlocal context #209

Merged
merged 41 commits into from
Jul 28, 2020

Conversation

satac2
Copy link
Contributor

@satac2 satac2 commented Jul 23, 2020

This is an implementation of the RuntimeContext and ThreadLocalContext which is used to provide access to a context object globally. @reyang

satac2 and others added 30 commits July 6, 2020 19:35
Co-authored-by: Reiley Yang <reyang@microsoft.com>
@satac2 satac2 requested a review from a team July 23, 2020 19:53
@codecov
Copy link

codecov bot commented Jul 23, 2020

Codecov Report

Merging #209 into master will increase coverage by 0.03%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #209      +/-   ##
==========================================
+ Coverage   92.31%   92.34%   +0.03%     
==========================================
  Files         102      102              
  Lines        3174     3188      +14     
==========================================
+ Hits         2930     2944      +14     
  Misses        244      244              
Impacted Files Coverage Δ
api/include/opentelemetry/context/context.h 98.14% <100.00%> (+0.03%) ⬆️
api/test/context/context_test.cc 100.00% <100.00%> (ø)

class RuntimeContext
{
public:
class Token
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to have a dtor for Token which calls Detach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That make sense, seems like it will add an element of safety so the context can't get stuck after a token has gone out of scope. I will add it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

Copy link
Member

@reyang reyang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@reyang reyang added the pr:please-merge This PR is ready to be merged by a Maintainer (rebased, CI passed, has enough valid approvals, etc.) label Jul 24, 2020
{
std::map<std::string, context::ContextValue> map_test = {{"test_key", (int64_t)123}};
context::Context test_context = context::Context(map_test);
context::RuntimeContext::Token old_context = context::RuntimeContext::Attach(&test_context);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the memory management strategy when managing context? Will contexts always be stored on the stack?

I'm afraid we might run into issues when having contexts stored on the stack, while managing all the context state via raw pointers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing I wonder about: whenever a new span is started, we have to set the current span. I think we can only do this by adding a new context. So we'll add a new context when a span is started and we end the context when the span is ended. The user though has the option to end a child span before a parent span. This shouldn't screw up our context stack. Maybe I'm also not completely understanding the workflow here, please correct me if I'm wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps RuntimeContext::Attach should take a context object instead of a ptr and that should prevent issues if the original context object goes out of scope.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the memory management strategy when managing context? Will contexts always be stored on the stack?

I think the context object has to be always allocated on the heap. Context can be propagated between threads and threads can get killed by the underlying OS (which means the stack will be reclaimed and repurposed).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user though has the option to end a child span before a parent span. This shouldn't screw up our context stack.

That's a very interesting topic, @pyohannes what would you expect for the following scenario?

// current span = EMPTY
span("A") begin
// current span = A
span("B") begin
// current span = B
span("C") begin
// current span = C
span("B") end
// current span = ???
span("A") end
// current span = ???
span("C") end
// current span = ???

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to RuntimeContext accepting a Context object instead of Context* so we should not be ref counting it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

*now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was originally thinking to unblock this PR first and then revisit

That's ok for me, as long as we keep track of open issues (memory management and asynchronous spans), preferably via GitHub issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we good to merge then?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#219 created for tracking.

virtual Token InternalAttach(Context *context) { return Token(); }

// Dummy method to be overriden by derived class implementation
virtual bool InternalDetach(Token &token) { return false; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make those methods pure virtual. Then the comment is not needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to pure virtual.

~Stack() { delete[] base_; }

int size_;
int capacity_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those should be size_t.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to size_t.

api/test/context/runtime_context_test.cc Show resolved Hide resolved
Copy link
Contributor

@pyohannes pyohannes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have some smaller nits.

Can you please submit an issue regarding the support of asynchronous segments (parent segments that end before their children). It seems the memory issue is accounted for by having copies of Context on the stack.


virtual Token InternalAttach(Context context) = 0;

virtual bool InternalDetach(Token &token) = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mark all methods as noexcept.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked as noexcept.


private:
// A nested class to store the attached contexts in a stack.
class Stack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's mark all Stack methods as noexcept. Top can be const too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marked as noexcept and const.


// A constructor that sets the token's Context object to the
// one that was passed in.
Token(Context context) { context_ = context; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a member initializer list here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to initializer list.

Context context_;

// Returns the stored context object.
Context GetContext() { return context_; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method isn't used anywhere. What would it be needed for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was initially used to get the context for comparison, but now that the comparison operator compares directly with contexts it is unnecessary, I will remove it.

class Token
{
public:
bool operator==(const Context &other) { return context_ == other; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it possible to compare a Token to a Context. I wonder if the argument should be of type Token?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was on purpose. There has not been a use case for token to token comparison.

{
size_ = 0;
capacity_ = 0;
base_ = nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use a member initializer list for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to initializer list.

for (auto i = 0; i <= old_size; i++)
{
temp[i] = base_[i];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use std::copy(base_, base_ + old_size, temp); instead of the loop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

@reyang reyang merged commit 283114d into open-telemetry:master Jul 28, 2020
ziqizh pushed a commit to ziqizh/opentelemetry-cpp that referenced this pull request Jul 31, 2020
ziqizh pushed a commit to ziqizh/opentelemetry-cpp that referenced this pull request Jul 31, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr:please-merge This PR is ready to be merged by a Maintainer (rebased, CI passed, has enough valid approvals, etc.)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants