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.
>