This is really like 3 messages, but I didn't want to spam the list with topics, 
and maybe I'm the only one feeling this pain anyway. So first: from the 
perspective of a guy who has dabbled in D1 and D2 for years, but hasn't looked 
closed at the language in ~10 months, I loved TDPL. It convinced me that D2 is 
going to kick ass. Sadly though, I felt like I finished chapter 13 without 
gaining any real understanding of how `shared` works in D, and how I can 
effectively share mutable data when it *is* necessary. Maybe this was 
intentional, and shared is just too much of an experimental feature still to 
write a chapter on. But I decided to play around with some prototypes.

First, it took me ages to find any sign of any locking primitives beyond the 
implied Mutex in Object. Finally I resorted to doing a `grep -ri semaphore 
/usr/local/dmd2` (on OS X), and found a whole wealth of code. There's an entire 
core.sync.* package! My end goal was to find or create a read/write lock, and 
lo and behold, there's one all ready and containing unit tests in 
core/sync/rwmutex.d.

Which leads to my first questions: why are the core.* interfaces apparently not 
documented along with the std.* packages on the D web site? Is there a 
documentation resource elsewhere? Even if the DDOC is sparse, at least showing 
which classes exist under core.* would be a huge help. And are these 
implementations ready for use by my code, or are they hidden away for a reason?

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.

Reply via email to