On Saturday, 11 February 2017 at 04:32:37 UTC, Michael Coulombe wrote:
On Friday, 10 February 2017 at 23:57:18 UTC, bitwise wrote:
[...]

A shared method means that it can only be called on a shared instance of the struct/class, which will have shared fields. A shared method should be logically thread-safe, but that cannot be guaranteed by the compiler. A non-shared method can touch shared memory, and thus should be thread-safe if it does, but can only be called on a non-shared instance with possibly non-shared fields.

shared/non-shared methods don't mix because you generally need to use different, less-efficient instructions and algorithms to be thread-safe and scalable in a shared method. In the case of Mallocator, there are no fields so as far as I can tell the attribute doesn't do much except for documentation and for storing references to it in other shared structs/objects.

Thanks for the explanation, but I'm still confused.

It seems like you're saying that 'shared' should mean both 'thread safe' and 'not thread safe' depending on context, which doesn't make sense.

Example:

shared A a;

struct A {
    int x, y;

    void foo() shared {
        a.x = 1;
    }
}

int main(string[] argv) {
    a.x = 5;
    a.y = 5;
    a.foo();
    return 0;
}

Qualifying 'a' with 'shared' means that it's shared between threads, which means that accessing it is _not_ thread safe. Since the method 'foo' accesses 'a', 'foo' is also _not_ thread safe. Given that both the data and the method are 'shared', a caller should know that race conditions are possible and that they should aquire a lock before accessing either of them...or so it would seem.

I imagine that qualifying a method with 'shared' should mean that it can access shared data, and hence, is _not_ thread safe. This prevent access to 'shared' data from any non 'shared' context, without some kind of bridge/cast that a programmer would use when they knew that they had aquired the lock or ownership of the data. Although this is what would make sense to me, it doesn't seem to match with the current implementation of 'shared', or what you're saying.

It seems that methods qualified with 'shared' may be what you're suggesting matches up with the 'bridge' I'm trying to describe, but again, using the word 'shared' to mean both 'thread safe' and 'not thread safe' doesn't make sense. Firstly, because the same keyword should not mean two strictly opposite things. Also, if a 'shared' method is supposed to be thread safe, then the fact that it has access to shared data is irrelevant to the caller. So 'shared' as a method qualifier doesn't really make sense. What would make more sense is to have a region where 'shared' data could be accessed - Maybe something like this:

struct S {
    shared int x;
    Lock lk;

    private void addNum(int n) shared {
        x += num;
    }

    int add(int a, int b)
    {
        shared {
            lk.lock();
            addNum(a);
            addNum(b);
            lk.unlock();
        }
    }
}

So above,
1) 'x' would be shared, and mutating it would not thread safe.
2) 'addNum' would have access to 'shared' data, and also be non-thread-safe 3) 'x' and 'addNum' would be inaccessible from 'add' since they're 'shared' 4) a 'shared' block inside 'add' would allow access to 'x' or 'addNum', with the responsibility being on the programmer to lock. 5) alternatively, 'shared' data could be accessed from within a 'synchronized' block.

I thought 'shared' was a finished feature, but it's starting to seem like it's a WIP. This kind of feature seems like it has great potential, but is mostly useless in it's current state. After more testing with shared, it seems that 'shared' data is mutable from many contexts, from which it would be unsafe to mutate it without locking first, which basically removes any gauruntee that would make 'shared' useful.

Again, tell me if I'm wrong here, but there seems to be a lot of holes in 'shared'.

  Thanks

Reply via email to