TDPL, shared data, and Phobos
Brian Palmer
d at brian.codekitchen.net
Sat Jul 17 08:10:05 PDT 2010
It probably wasn't very clear from my simplified example, but I'm looking to create a shared-reader-one-writer scenario. If I declare MyValue synchronized, only one thread can be inside the get() method at a time, which defeats the shared-reader requirement. Imagine this is a much larger more complex data structure, where get() requires walking through multiple levels of a tree and a binary search at the last level.
-- Brian
Bane Wrote:
> I am few days old in playin with D2 and whole shared stuff, so I am probably wrong in something.
>
> You should probably declare your example class MyValue synchronized instead of shared. It implies that class is shared too, and this way all methods are synchronized. In D1 you could mix synchronized and non syncrhonized methods in class, in D2 its whole or nothing. This way you don't need _lock var in your example.
>
> So this would work (i guess)
>
> synchronized class MyValue {
> int inc() {
> return _value++;
> }
> int get() {
> return _value;
> }
> private int _value;
> }
>
> shared MyValue sharedVal;
>
> void main(){
> sharedVal = new shared(MyValue );
> }
>
> I noticed that in D1 synchronized methods of same class share same lock, while in this D2 example (when the whole class is declared synchronized), each method has its own lock.
>
> > Also, is there any documentation on the actual semantics of shared? http://www.digitalmars.com/d/2.0/attribute.html is a blank on the subject, and the "migrating to shared" article only talks about simple global state. What are the actual semantics of shared classes, and how do they interact with other code? For instance, after much banging of my head against the desk, I finally wrote a working implementation of a simple shared multi-reader var. Obviously there are better ways to do a simple shared incrementing counter, this is just a first experiment working toward a shared mutable 512MB trie data structure that we have in our app's current C++ implementation:
> >
> > > shared class MyValue {
> > > this() {
> > > _lock = cast(shared)new ReadWriteMutex;
> > > }
> > >
> > > int inc() {
> > > synchronized((cast(ReadWriteMutex)_lock).writer) {
> > > return _value++;
> > > }
> > > }
> > >
> > > int get() {
> > > synchronized((cast(ReadWriteMutex)_lock).reader) {
> > > return _value;
> > > }
> > > }
> > >
> > > private ReadWriteMutex _lock;
> > > private int _value;
> > > }
> > >
> > > shared MyValue sharedVal;
> > > ... seems to behave correctly with multiple threads reading and writing ...
> >
> > So I can maybe understand the cast(shared) in the ctor. But I have to admit I have absolutely no idea why I had to cast away the shared attribute in the inc/get methods. Is there any documentation on what's really going on in the compiler here? It's a shared method, accessing a shared instance var, why the cast? Is the compiler upset about something in the definition of ReadWriteMutex itself?
> >
> > Also, how would one implement this as a struct? My postblit op generates compiler errors about casting between shared/unshared MyValue:
> >
> > > shared struct MyValue {
> > > this(this) { _lock = cast(shared) new ReadWriteMutex; } // ERROR
> > > ... same as above ...
> > > }
> >
> > I recognize the possible race conditions here, but there has to be *some* way to implement a postblit op on a shared struct?
> >
> > I hope this doesn't come across as empty complaining, I'm happy to help improve the documentation if I can.
>
More information about the Digitalmars-d
mailing list