1

Let's start with a piece of code (Coliru):

#include <iostream> #include <thread> using namespace std; struct A { thread_local static A* p_a; thread_local static int i; }; thread_local int A::i; thread_local A* A::p_a; int main( ) { A::p_a = new A; auto lambda = [](A* a) { a->i = 1; // Prints 1 (below, of course) std::cout << a->i << std::endl; }; std::thread t(std::bind(lambda, A::p_a)); t.join(); // Prints 0 (it hasn't been modified) std::cout << A::p_a->i << std::endl; return 0; } 

As all of you can see, the second thread modifies its thread local copy of A::i, even though I accessed it from another thread local object of another thread. Is it the expected behaviour? Because it makes impossible to get info from another thread using a "referer" unless I pass a pointer or reference to the foreign's thread_local object which I want to read.

With "referer" I refer something which manages or can give you access to its own thread_local variable from its thread. But that's impossible!! Any expression yielding a thread_local variable, no matter from whom (I have done different test, even with accessor functions), ends by using the thread_local instance of the reading thread.

4
  • 1
    I am not sure using a thread_local to communicate between threads is a in keeping with the spirit of thread_local storage. Why not just use a normal static? Commented Sep 18, 2016 at 12:09
  • I'm just.... "understanding the behaviour" (or trying to). Commented Sep 18, 2016 at 12:27
  • 1
    Ah well its a syntax issue. Just because you are using syntax that looks like you are dereferencing an A pointer, you're not. The compiler sees it is a static and ignores the pointer and goes straight for the single (per thread in this case) static instance. The whole thread_local business is actually irrelevant to that. Commented Sep 18, 2016 at 12:34
  • Make i a regular non static and non thread_local member. Then your lambda will be referring to the i from the instance of A created in main. Commented Sep 18, 2016 at 13:53

2 Answers 2

6

This is a syntax issue: In this case a->i; is identical to A::i; because A::i is a static member and its address is not dependant on any one instance of A.

So just because you are using syntax that looks like you are dereferencing an A pointer, you're not. The compiler sees it is a static member and ignores the pointer and goes straight for the single (per thread in this case) static instance. The whole thread_local business is actually irrelevant to that.

So when you are accessing a static member of A through A* a in your lambda, the compiler is ignoring the address you gave it and doing A::i regardless (getting its own thread_local version).

struct A { static int i; }; A* a = new A; a->i; // identical to A::i (because i is static) 

This is standard syntax as mentioned here in the C++14 Standard:

5.2.5 Class member access [ expr.ref ]

1. A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template (14.2), and then followed by an id-expression, is a postfix expression. The postfix expression before the dot or arrow is evaluated; 65 the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression.

...

65) If the class member access expression is evaluated, the subexpression evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.

(emphasis mine)

Sign up to request clarification or add additional context in comments.

5 Comments

But that's a "compiler" thing? Or is it present on the standard?
@Peregring-lk Its part of the syntax. If you think about it there is only one version of static members per class. So how can the compiler care about which address you access that single instance? With thread_local it simply means there is only one per thread but the problem remains. Accessing static members through -> doesn't use the pointer, it just uses the class type to find the static (or thread_local static) instance.
Yes. my comment deleted as obsolete. This one will follow shortly.
Yes its clearer. I was just wondering if you replied because of your "own deductions" or that's the "designed" behaviour. Thinks that the dereference operator is being ignored; it is just seen to track which object the expression refers to; it's like searching "by name". Thinks that "p_a" could be a custom made pointer wrapper with an overloaded operator* and so on; the "syntax sugar" explanation will be harder to defend (what if there's an if involved before returning the reference?). But anyway, in any case, I'm convinced the used var will be the thread local one and no the external one.
@Peregring-lk I just included an excerpt from the C++14 Standard confirming this syntax is legal when accessing static members. Regarding overloading operator-> well that just yields another pointer to which the -> is finally applied. I would imagine (though not confirmed) the type of the yielded pointer determines which class' static member is accessed in that case.
0

you passed one 'A' porinter, but we should know that 'i' variable and 'p_a' variable are not belong 'A' actually, then are static, so although you started a thread passed by a 'A' pointer,then modified 'i' variable, it is different ,because this 'i' is not outside 'i',they are different.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.