Does anyone understand how to use "shared" types with concurrency send/receive functions?

Arek via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Mon Aug 14 12:49:30 PDT 2017


On Monday, 14 August 2017 at 03:59:48 UTC, Jonathan M Davis wrote:
> On Sunday, August 13, 2017 16:40:03 crimaniak via 
> Digitalmars-d-learn wrote:
>> More of this, I think, you can't avoid __gshared for any 
>> complex work. Even mutexes from Phobos doesn't support shared, 
>> so I had to 'cowboy with __gshared' when implementing my site 
>> engine.
>
> The way to handle shared is to protect the section of code 
> that's using the shared object with either a mutex or 
> synchronized block, and then you cast away shared from the 
> object within that section and operate on it as thread-local. 
> When you're done, you make sure that you don't have any 
> thread-local references to the data, and you release the mutex 
> or exit the synchronized block. e.g. something like
>
> shared T sharedObj = getSharedObj();
>
> synchronized(mutex)
> {
>     T nonSharedObj = cast(T)sharedObject
>
>     // do stuff...
>
>     // make sure that no references to nonSharedObj have escaped
> }
>
> // now, there's just the shared version of the object
>

Yeah, and this is what i'm  doing now (more or less).
To be more precise, I don't even want to synchronize access to 
the shared resource between the threads. I just want to move the 
object from one thread to another.
Of course I could copy the local object, but my obcjecs has 
indirect references to others, forming kind of tree.

I like the idea of channels in Go. I've tried to get something 
similiar with send/receive.
In this case, object could be also immutable, because once there 
are created (in the deserialization process) they will no be 
modified. I just have to emit them into another task (to be 
honest, I use fiber, so it's not even another thread, and they 
will not be accessed in parallel).

But all this language protections makes the issue unexpectedly 
complicated.



> And no, this isn't ideal, but the only semi-decent solution 
> that's been proposed that safely casts away shared for you is 
> synchronized classes, which Andrei describes in TDPL but have 
> never been implemented. And because they can only safely strip 
> off the outermost layer of shared, they're of questionable 
> usefulness anyway. Ultimately, even with synchronized classes, 
> in many situations, the programmer is going to have to 
> carefully cast away shared to operate on the object within a 
> protected context.
>
> Now, the fact that the mutex objects don't handle shared 
> correctly is another issue entirely. Having to cast away shared 
> from mutexes is dumb, because you're obviously not going to be 
> protecting them with a mutex, and their operations have to be 
> atomic anyway for them to do what they do. So, that definitely 
> needs to be fixed. However, I believe that it _has_ been fixed 
> in master, and it might have made it into a release now, but 
> I'm not sure. So, core.sync.mutex.Mutex _should_ now be useable 
> as shared like it should be.
>
> In general though, the idea is that you simply don't operate on 
> shared objects except via atomic operations. Otherwise, you 
> risk concurrency problems. And really, this is the same as what 
> you'd do in C/C++, except that in C/C++, it doesn't catch you 
> when you operate on an object that's shared across threads with 
> non-atomic operations (because the object isn't explicitly 
> typed as shared), and you don't have to cast away shared to do 
> non-atomic operations. So, having to cast away shared is the 
> price of getting the protection against accidentally using 
> non-atomic operations on a shared object as well as the price 
> we pay to be able to have the type system distinguish between 
> shared and thread-local objects so that it's able to optimize 
> based on the knowledge that an object is thread-local. 
> Ultimately though, you're doing the same thing that you'd do in 
> C++ if you're handling concurrency safely. You just have to 
> explicitly mark stuff as shared and carefully cast away shared 
> in certain, protected contexts.
>
> Using __gshared in extern(D) code is just asking for it, 
> because then you have an object that the compiler thinks is 
> thread-local but isn't, and you risk subtle and nasty bugs as a 
> result. __gshared is only intended for binding to extern(C), 
> global variables. To an extent, you can get away with using it 
> with extern(D) variables, but that's not its intended purpose, 
> and you risk running afoul of the compiler and what it chooses 
> to do based on the assumption that the object is thread-local.
>
> - Jonathan M Davis

Thanks for the explanation! It would be good to have a 
comprehensive article on this subject.

Arek


More information about the Digitalmars-d-learn mailing list