D const enables multi-reader synchronization

Tomek Sowiński just at ask.me
Tue Jun 15 13:38:58 PDT 2010


Brad Roberts wrote:

> On Mon, 14 Jun 2010, Tomek Sowi?ski wrote:
> 
>> Dnia 14-06-2010 o 21:27:24 Brad Roberts <braddr at slice-2.puremagic.com>
>> napisa?(a):
>> 
>> > Const methods only prevent mutating 'this'. You can still call
>> > functions that mutate other state (via globals or passed in arguments).
>> 
>> But synchronized on a method also protects only 'this', no?. Currently
>> you can have:
>> 
>> class A { ... }
>> 
>> shared class K {
>>    synchronized void fun(A a) const {
>>        // mutate a
>>    }
>> }
>> 
>> If you call fun on two instances of K, each in a different thread, but
>> pass them the same instance of A, you'll get a data race anyway. You
>> could make fun's arguments const, but still there's shared globals.
> 
> The lock is per-object but the lock protects all of the code inside the
> block, not just the parts that hang off the this reference.
> 
> You're code describes the case I'm talking about.  If the mutations to A
> are all inside the K::fun code, and your proposal happens, then code that
> runs safely changes to code that runs unsafely.
> 
> I'm not saying it's a good idea to structure code that way, but the
> language rules need to consider all angles, not just the used correctly or
> best practices uses.

Hm, not sure if we understand each other. Perhaps a full example:

import std.stdio;
import core.thread;

class A { int a; }

shared class K {
    A a;
    synchronized void fun(A a) const {
        foreach (i; 0..3) {
            writeln(Thread.getThis().name, ": I saw ", a.a, ", changed to ", 
++a.a);
            Thread.sleep(1_000_000);
        }
    }
}

class Thr : Thread {
    A a;

    this(A a, string name) {
        super(&run);
        this.a = a;
        this.name = name;
    }

    void run() {
        auto k = new K;
        k.fun(a);
    }
}

void main() {
    A a = new A;
    (new Thr(a, "T1")).start();
    (new Thr(a, "T2")).start();
}

Compiled with DMD 2.045 it outputs:
T2: I saw 0, changed to 1
T1: I saw 1, changed to 2
T2: I saw 2, changed to 3
T1: I saw 3, changed to 4
T2: I saw 4, changed to 5
T1: I saw 5, changed to 6

So the 2 sync'ed calls are mutating its parameter at the same time, already 
without my proposal.

Anyway, language-level implementation probably doesn't make sense. As Sean 
said, it's too much machinery and too many options to devise a standard that 
would make everybody happy.


Tomek


More information about the Digitalmars-d mailing list