Jump to page: 1 2
Thread overview
[dmd-concurrency] tail-shared by default?
Jan 09, 2010
Walter Bright
Jan 09, 2010
Walter Bright
Jan 09, 2010
Walter Bright
Jan 09, 2010
Walter Bright
Jan 09, 2010
Walter Bright
Jan 09, 2010
Sean Kelly
Jan 09, 2010
Sean Kelly
[dmd-concurrency] sharedRead() and sharedWrite() or not?
Jan 09, 2010
Michel Fortin
Jan 09, 2010
Sean Kelly
Jan 09, 2010
Walter Bright
Jan 09, 2010
Michel Fortin
Jan 09, 2010
Sean Kelly
Jan 10, 2010
Michel Fortin
January 08, 2010
I don't think you read my points then.

any shared reference type that is not global is tail-shared.  global variables are the only way to have head-shared references. I outline this clearly in the previous posts.

-Steve

On Fri Jan 8th, 2010 7:03 PM EST Walter Bright wrote:

>
>
>Steve Schveighoffer wrote:
>> 
>> It's too bad that tail-const wouldn't work.  I don't think tail-shared presents the same problems.  Please don't dismiss all the points I wrote just because of past failures.  They are new ideas that don't apply to const at all.
>> 
>> 
>
>They do apply. It is not about the semantic difference between const and shared, it is about the question "is this expression shared or not shared? const or not const?" How do I declare a type to be tail-shared? tail-const? Those questions must be unambiguously answered before you can apply semantics.
>
>In this context, tail-shared and tail-const are exactly the same problem.
>_______________________________________________
>dmd-concurrency mailing list
>dmd-concurrency at puremagic.com
>http://lists.puremagic.com/mailman/listinfo/dmd-concurrency




January 08, 2010
All that's necessary is to take the address of a shared local and pass it to another thread:

   void foo()
   { shared int x;
      passToAnotherThread(&x);
      x++;     // <=== synchronization issues!
      waitForThreadToFinish();
    }

Steve Schveighoffer wrote:
> I don't think you read my points then.
>
> any shared reference type that is not global is tail-shared.  global variables are the only way to have head-shared references. I outline this clearly in the previous posts.
>
> -Steve
>
> On Fri Jan 8th, 2010 7:03 PM EST Walter Bright wrote:
>
> 
>> Steve Schveighoffer wrote:
>> 
>>> It's too bad that tail-const wouldn't work.  I don't think tail-shared presents the same problems.  Please don't dismiss all the points I wrote just because of past failures.  They are new ideas that don't apply to const at all.
>>>
>>> 
>>> 
>> They do apply. It is not about the semantic difference between const and shared, it is about the question "is this expression shared or not shared? const or not const?" How do I declare a type to be tail-shared? tail-const? Those questions must be unambiguously answered before you can apply semantics.
>>
>> In this context, tail-shared and tail-const are exactly the same problem.
>> _______________________________________________
>> dmd-concurrency mailing list
>> dmd-concurrency at puremagic.com
>> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
>> 
>
>
>
> 
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
>
>
> 
January 08, 2010
----- Original Message ----

> From: Walter Bright <walter at digitalmars.com>
> 
> All that's necessary is to take the address of a shared local and pass it to another thread:
> 
>   void foo()
>   { shared int x;
>      passToAnotherThread(&x);
>      x++;     // <=== synchronization issues!
>      waitForThreadToFinish();
>    }
> 

In my scheme the declaration of x above is illegal.  You cannot share stack data.

Even if you have:

void foo()
{
   shared int * x; // ok, equivalent to shared(int)*
   passToAnotherThread(&x); // error, &x is shared(int)**, cannot convert to shared (int *)*
}

basically, it's impossible to have a fully shared local variable, all shared local variables are tail-shared.  The only place fully shared data can be is global data or the heap.  This is in essence exactly how it should be -- you are not sharing the pointer that you copy onto the stack, you are sharing what it points to.

-Steve




January 08, 2010

Steve Schveighoffer wrote:
> ----- Original Message ----
>
> 
>> From: Walter Bright <walter at digitalmars.com>
>>
>> All that's necessary is to take the address of a shared local and pass it to another thread:
>>
>>   void foo()
>>   { shared int x;
>>      passToAnotherThread(&x);
>>      x++;     // <=== synchronization issues!
>>      waitForThreadToFinish();
>>    }
>>
>> 
>
> In my scheme the declaration of x above is illegal.  You cannot share stack data.
>
> Even if you have:
>
> void foo()
> {
>    shared int * x; // ok, equivalent to shared(int)*
>    passToAnotherThread(&x); // error, &x is shared(int)**, cannot convert to shared (int *)*
> }
>
> basically, it's impossible to have a fully shared local variable, all shared local variables are tail-shared.  The only place fully shared data can be is global data or the heap.  This is in essence exactly how it should be -- you are not sharing the pointer that you copy onto the stack, you are sharing what it points to.
>
> -Steve
>
> 

Tail-shared doesn't work as soon as classes come into the picture (or any reference types).
January 08, 2010
----- Original Message ----

> From: Walter Bright <walter at digitalmars.com>
> 
> Tail-shared doesn't work as soon as classes come into the picture (or any reference types).

class C
{}

void foo()
{
   shared C c = new shared(C);
}

In my scheme, c is tail-shared.  All reference types, including pointers and class references, are tail-shared (note the subject line).  The only exception is shared global data.

-Steve




January 08, 2010

Steve Schveighoffer wrote:
> ----- Original Message ----
>
> 
>> From: Walter Bright <walter at digitalmars.com>
>>
>> Tail-shared doesn't work as soon as classes come into the picture (or
>> any reference types).
>> 
>
> class C
> {}
>
> void foo()
> {
>    shared C c = new shared(C);
> }
>
> In my scheme, c is tail-shared.  All reference types, including pointers and class references, are tail-shared (note the subject line).  The only exception is shared global data.
>
> 

I hate to say it, but that simply doesn't work in any consistent manner. Types must be composable, with composable properties. Having types dependent on where they are declared is a pending disaster (what about type aliases, template type parameters, etc.?). Having no way to specify that c itself is shared is a disaster (how would you compose a C* ? Where does the shared go?).

I've been down this path before. It doesn't work. I learned my lesson <g>. I know it's not obvious why it fails, which is why I spent so much time trying to make it work.
January 08, 2010
----- Original Message ----

> From: Walter Bright <walter at digitalmars.com>
> 
> Steve Schveighoffer wrote:
> > ----- Original Message ----
> > 
> > 
> >> From: Walter Bright
> >> 
> >> Tail-shared doesn't work as soon as classes come into the picture (or any
> reference types).
> >> 
> > 
> > class C
> > {}
> > 
> > void foo()
> > {
> >    shared C c = new shared(C);
> > }
> > 
> > In my scheme, c is tail-shared.  All reference types, including pointers and
> class references, are tail-shared (note the subject line).  The only exception is shared global data.
> > 
> > 
> 
> I hate to say it, but that simply doesn't work in any consistent manner. Types must be composable, with composable properties. Having types dependent on where they are declared is a pending disaster

I just realized the type isn't dependent, the semantics of access are.  The type of globals can still be tail-shared, but accessing a tail-shared global variable results in barriers around the access, since it lives in a shared space (see the model for GDATA below).

> (what about type aliases, template type parameters, etc.?). Having no way to specify that c itself is shared is a disaster (how would you compose a C* ? Where does the shared go?).

The only time the reference c itself uses shared semantics is when it's a stored as a global or static variable.  You can never specify that the *reference* is shared otherwise (to do so makes no sense to me).

aliases and template parameters all define shared the same way.  The non-tail-shared class reference does not exist as a type, because the only shared reference type is tail-shared.

For instance, in IFTI, if you did this:

foo(T)(T t) {writeln(typeid(T));}

class C
{
   int x;
}

void main()
{
   shared C c = new shared(C);
   foo(c);
   foo(&c.x);
   foo(c.x);
}

You should get:

shared(C)
shared(int)*
int

where the first 2 are tail-shared.

Here is another way to think about it.  Pretend all global shared data goes into a structure called GDATA, and there is exactly one singleton reference to that data.  That reference is a tail-shared reference.  It might look like this:

shared int x;
shared C c;

translates to

struct GDATA
{
   int x;
   C c;
}

__gshared shared(GDATA) * globalNamespace;

Any access to x or c becomes globalNamespace.x or globalNamespace.c.

This is exactly how I see the underlying implementation anyway.

if you pass an address to globalNamespace.x, that address is of type shared(int)*, not shared(int *), because shared(int *) makes the stack variable shared right out of the gate!  There is no reason to do that, *and* sharing stack data is a recipe for memory corruption.

shared has 2 features -- one is the type propogation to ensure you don't lose the fact you are dealing with shared data, and the other is the compiler's treatment of access to shared data (i.e. adding barriers).  The latter can be different between contexts of where a variable is declared because the compiler has access to that info, the former must be consistent across all contexts.  My scheme is consistent for the former, and uses the context available to the compiler to ensure the latter is safe.

> I've been down this path before. It doesn't work. I learned my lesson . I know it's not obvious why it fails, which is why I spent so much time trying to make it work.

It's a completely different idea than tail-const as an *option*.  Here, tail-shared is the *only* option.  I know it's not obvious that it's completely different from const, but it is.  Trust me, I understand the reluctance to look at it because I spent a lot of time trying to come up with a good way to do tail-const also.  Just try to find an example that proves it doesn't work, and you will see that it's unbreakable.

I think the idea is sound because you *must* pass a pointer to shared data into a function, you can't actually pass the real data, so the pointer itself that lives on the stack should *never* be shared, it's always thread local.  Sharing stack data would be more of a pending disaster in my opinion, since stack data is deallocated at will by returning from a function!

-Steve




January 08, 2010

Steve Schveighoffer wrote:
> I think the idea is sound because you *must* pass a pointer to shared data into a function, you can't actually pass the real data, so the pointer itself that lives on the stack should *never* be shared, it's always thread local.  Sharing stack data would be more of a pending disaster in my opinion, since stack data is deallocated at will by returning from a function!
>
> 

Think about passing a local by reference to another function, i.e. ref parameters.

If I understood you correctly, you are not talking about adjusting the type, but about having something not be shared because it's a local. Having special case rules often sound good, but later turn out to have unforeseen consequences (C++ is full of them). They are best avoided as much as possible.
January 09, 2010

Walter Bright wrote:
>
> If I understood you correctly, you are not talking about adjusting the type, but about having something not be shared because it's a local. Having special case rules often sound good, but later turn out to have unforeseen consequences (C++ is full of them). They are best avoided as much as possible.
>

One egregious example of that I wrote about recently is C's conflating of arrays with pointers under some circumstances.
January 09, 2010
I think this will all actually work out if the compiler detects when a shared value doesn't escape its local scope and thus doesn't need synchronization. If I have a private shared class member of a local class instance then the compiler should reliably be able to determine that the reference itself isn't visible yo another thread and therfore doesn't need to be atomic.  Calls to the shared class instance will still need to be restricted to shared and synchronized members, but the call won't need to be atomic(a).foo().

Really, the only time references may need to be atomic is if they are public members of a shared instance or if they are global.

Sent from my iPhone

On Jan 8, 2010, at 11:36 PM, Walter Bright <walter at digitalmars.com> wrote:

>
>
> Steve Schveighoffer wrote:
>> I think the idea is sound because you *must* pass a pointer to shared data into a function, you can't actually pass the real data, so the pointer itself that lives on the stack should *never* be shared, it's always thread local.  Sharing stack data would be more of a pending disaster in my opinion, since stack data is deallocated at will by returning from a function!
>>
>>
>
> Think about passing a local by reference to another function, i.e. ref parameters.
>
> If I understood you correctly, you are not talking about adjusting
> the type, but about having something not be shared because it's a
> local. Having special case rules often sound good, but later turn
> out to have unforeseen consequences (C++ is full of them). They are
> best avoided as much as possible.
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
« First   ‹ Prev
1 2