www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - TDPL, shared data, and Phobos

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

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.
Jul 13 2010