Difference between __gshared and shared.

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Thu Jul 9 07:03:13 PDT 2015


On Thursday, 9 July 2015 at 12:39:00 UTC, Márcio Martins wrote:
> On Wednesday, 8 July 2015 at 21:15:19 UTC, deadalnix wrote:
>> On Wednesday, 8 July 2015 at 12:08:37 UTC, Jonathan M Davis 
>> wrote:
>
>>> I know that there are a number of people who get frustrated 
>>> with shared and using __gshared instead, but unless you fully 
>>> understand what you're doing and how the language works, and 
>>> you're _really_ careful, you're going to shoot yourself in 
>>> the foot it subtle ways if you do that.
>>>
>>> - Jonathan M Davis
>>
>> Amen
>
> What sort of subtle ways? Can you give examples that are not 
> effectively the same subtle ways you would encounter with 
> pthreads in C/C++? I have been running with the assumption that 
> __gshared effectively bypasses TLS, which again, feels sort of 
> dirty to use a __ prefixed keyword for that, but, yeah...

Well, the compiler is free to assume that a variable that is not 
marked as shared is thread-local. So, it's free to make 
optimizations based on that. So, for instance, it can know for a 
fact that

auto foo = getFoo();
auto result1 = foo.constPureFunction(); // This function _cannot_ 
mutate foo
auto result2 = foo.constPureFunction(); // This function _cannot_ 
mutate foo
auto bar = foo;

So, it knows that the value of bar is identical to the value of 
foo and that result1 and result2 are guaranteed to be the same, 
because it knows that no other thread can possibly have mutated 
foo within this code, and there's no way that this code mutated 
foo even through another reference to the same data on the same 
thread. And it can know that thanks to how const, pure, and TLS 
all work. The compiler is free to optimize the code or make other 
alterations to it based on that knowledge, so if it makes an 
optimization based on that, and foo is actually shared across 
threads (either because the object it refers to was originally 
__gshared or because shared was cast away incorrectly), then 
you're going to have incorrect machine code. And what 
optimizations the compiler does with code like this could change 
over time. And unless you're an expert in the language and in the 
compiler, you're not going to know when the compiler is going to 
make optimizations where the fact that the variable is in TLS 
factors in. So, you're not going to know when the compiler might 
optimize your code in ways that won't work with __gshared, and 
what optimizations it does or doesn't do right now won't 
necessarily be the same ones that it does or doesn't do later.

You have the same problem with shared, but in that case, the 
compiler makes it so that you have to cast away shared to get 
into this mess. It protects against doing stuff like accidentally 
passing a shared object around into code that will treat is a 
thread-local. Heck, with a __gshared object, if you change its 
type, it could go from being a value type where passing it to 
other code works just fine because it's truly copied to being a 
reference type (or partial reference type) where it's not copied 
(or only partially copied), and the compiler won't be able to 
help you catch the points where you were doing a full copy before 
but aren't now. And if it's your coworker that changed the 
definition of the type of the variable that you marked as 
__gshared, you could be screwed without knowing it.

Really, we can't tell what subtle behavioral problems you're 
risking with __gshared, because that depends on what the compiler 
is currently able to do with the assumption that a variable is in 
TLS. You run into all of the problems that you risk with sharing 
variables in threads in C++ only worse, because the D compiler is 
free to assume that an object is thread-local unless it's marked 
as shared and thus can make optimizations based on that, whereas 
the C++ compiler can't. And you've thrown away all of the 
compiler's help by using __gshared. __gshared is intended 
specifically for use with interacting with C code where we don't 
really have a choice, and you have to be careful with it. For 
everything else, if you need to share data across threads, that's 
what shared is for, and the compiler then knows that it's shared, 
so it will optimize differently, and it'll yell at you when you 
misuse it. Ultimately, you still have the risk of screwing it up 
when you cast away shared when the object is protected by a lock, 
but then at least, even if you end up with a subtle bug, because 
a thread-local reference to the shared data escaped the lock, 
it's a lot easier to figure out where you've misused shared 
incorrectly in D than figuring out where you might have screwed 
it up in C++, because all of your shared objects are explicitly 
marked as such, and it's the points where you cast away shared 
that risk problems, so you have a lot less code to look at.

As annoying as it can be, shared is your friend. __gshared is not.

- Jonathan M Davis


More information about the Digitalmars-d mailing list