On Friday, 1 February 2013 at 04:22:51 UTC, Zach the Mystic wrote:
On Friday, 1 February 2013 at 03:39:19 UTC, TommiT wrote:
On Friday, 1 February 2013 at 03:13:42 UTC, Zach the Mystic wrote:
On Friday, 1 February 2013 at 02:27:30 UTC, TommiT wrote:
And here's another example of why it is as big of a problem as I make it sound to be:

import std.concurrency;

struct Array
{
 int _len;

 length struct // Using Zach's syntax
 {
     @property get() { return _len; }
     alias this = get;
     void opAssign(int rhs) { _len = rhs; }
 }
}

void func(T)(T t)
{
 auto v = t;
 v = 10;
}

void main()
{
 Array arr;
 spawn(&func!int, arr.length);
 arr.length = 20; // And here we have a data-race.
                  // Good luck trying to find these
                  // kinds of bugs.
}

I'm sorry, I know very little about race conditions. If you might explain just a little bit about what is happening here, I'd be in a better position to understand what you're saying. I really can't say anything other than please describe what this does and why it's a problem at this time.

spawn(&func!int, arr.length);

...creates a new thread and runs the following function call in it:
func!(int)(arr.length)

While that function call is evaluating in the new thread, the old thread may simultaneosly execute:
arr.length = 20;

Since both of those threads end up at some point calling arr.length.opAssign and therefore both of them may assign to arr._len simultaneously, that's a data-race. It would be data-race also if only one of them wrote to it and the other one just read it.

I'm familiar with the fact that programmers face endless woes with regard to data-races. I thought that D has unshared data by default. But since Array arr is defined in main instead of outside main, therefore it is sharable between the two threads? If two threads have access to the same data, what makes length as a property different from length as an int? This problem may be over my head for now...

Actually, that example of mine was wrong - it's not a data-race. I failed to notice that in:
spawn(&func!int, arr.length);
...arr.length is actually converted to int using alias this. My bad.

Now I'm going to repeat the "spooky modification at a distance" problem I showed earlier, but this time, instead of writing it in the new syntax and the new struct behaviour you invented, I write it using current D syntax:

import std.concurrency;

struct Array
{
    int _len;

    struct PropType
    {
        Array* _outer; // In your syntax this was implicit

        @property int get()
        {
            return _outer._len;
        }

        alias this = get;

        void opAssign(int rhs)
        {
            _outer._len = rhs;
        }
    }

    PropType length;
}

void func(T)(T t)
    if (isImplicitlyConvertible!(T,int))
{
    // Given that is(T == Array.PropType), then
    // all 3 lines below do the same thing:
    t = 42;
    t.opAssign(42);
    t._outer._len = 42;
}

void main()
{
    Array arr;

    // setting the _outer pointer to the enclosing object:
    arr.length._outer = &arr;

    assert(arr._len == 0);
    func(arr.length); // no conversion to int happening here
    assert(arr._len == 42);
}

So, it's possible to modify arr._len just by passing arr.length by value to a function.

Reply via email to