January 07, 2010
----- Original Message ----

> From: Michel Fortin <michel.fortin at michelf.com>
> Le 2010-01-07 ? 15:34, Steve Schveighoffer a ?crit :
> 
> > In fact, any shared class reference on the stack erroneously will create
> memory barriers around the stack variable itself -- you shouldn't be sharing any stack data.
> 
> I'm not sure you should never be sharing stack data. Stack data can already be allocated on the heap with closures. If you're going to give that closure to another thread, the data it uses needs to be shared.

If you pass a closure to another thread, the whole stack frame should be marked as shared because you are passing the whole stack frame.  The only reason to mark individual variables as shared is if you are passing addresses of individual variables to other threads, which you shouldn't do.

e.g.:

void foo()
{
  int x;
  shared int y;
  void subfoo()
  {
     x++;
    atomic(y++);
  }
  passToOtherThread(&subfoo);
}

The other option is to mark an inner function somehow so it can *only* access shared variables, and then it somewhat makes sense, but I don't see that it's an important feature.  Allowing shared stack variables seems to be erroneous in most cases, and causes too much confusion for the sake of some bizarro delegate scheme.

You also would never ever allocate such a function frame on the stack :)  At least the shared portions.

In fact, I'd suggest that any time you see this:

void foo()
{
   int x;
   shared int y;
}

It's either a compiler error, or the portion of foo's stack frame that contains y should always be globally heap-allocated.  It is acceptable to allocate x on the stack, but it can go into the heap frame if it makes things easier.  However, delegates which access x have to be unshareable (but could access y through normal shared means).

-Steve




January 07, 2010
Sean Kelly wrote:
[snip]

shared(A) a;

and

shared A a;

are the same thing.


Andrei
January 07, 2010
On Jan 7, 2010, at 1:18 PM, Michel Fortin wrote:

> Le 2010-01-07 ? 15:34, Steve Schveighoffer a ?crit :
> 
>> In fact, any shared class reference on the stack erroneously will create memory barriers around the stack variable itself -- you shouldn't be sharing any stack data.
> 
> I'm not sure you should never be sharing stack data. Stack data can already be allocated on the heap with closures. If you're going to give that closure to another thread, the data it uses needs to be shared.

I knew there was an instance where I might want to declare a delegate as shared.  Maybe this could be handled just like scope?
January 07, 2010
On Jan 7, 2010, at 2:46 PM, Andrei Alexandrescu wrote:

> Sean Kelly wrote:
> [snip]
> 
> shared(A) a;
> 
> and
> 
> shared A a;
> 
> are the same thing.

Nuts!  So we'd need a wrapper struct to deal with this?
January 07, 2010
On Jan 7, 2010, at 2:10 PM, Steve Schveighoffer wrote:

> ----- Original Message ----
> 
>> From: Michel Fortin <michel.fortin at michelf.com>
>> Le 2010-01-07 ? 15:34, Steve Schveighoffer a ?crit :
>> 
>>> In fact, any shared class reference on the stack erroneously will create
>> memory barriers around the stack variable itself -- you shouldn't be sharing any stack data.
>> 
>> I'm not sure you should never be sharing stack data. Stack data can already be allocated on the heap with closures. If you're going to give that closure to another thread, the data it uses needs to be shared.
> 
> If you pass a closure to another thread, the whole stack frame should be marked as shared because you are passing the whole stack frame.

While some people may want to cowboy and send a "scope delegate" to another thread, I can assure you this won't be possible in any Phobos messaging API.  It's too much of a recipe for disaster.  Following the per-thread GC model I can see even accepting a "shared delegate" to indicate that it will be passed to another thread.
January 07, 2010
On Jan 7, 2010, at 1:57 PM, Steve Schveighoffer wrote:
> 

>> I'd think it would be pretty common to want a local reference to shared data. Then you wouldn't have to pay for the atomic read required if the reference itself is shared.
> 
> I think more than const, we need something like this for shared.

Yes.  If it can be done with a wrapper like Rebindable then great.  But not having any way around this would really stink.

>> Yeah, a fully shared field inside a non-shared class doesn't sound right.  The smoke test for me is that I think about whether it would work with per-thread GCs.  If one thread has a reference to "shared" data that actually lives in another thread's heap or stack, there's a dangling reference issue if the thread terminates (at least in theory--the heap data could easily be handed off to the shared GC instead, but let's pretend this is impossible).
> 
> Yeah, allowing this seems to imply that the global heap will have to be scanned for local heap collections:
> 
> class C
> {
>   shared(int)* ptr1;
>   int *ptr2;
> }
> 
> I assume that marking a single member shared makes C get allocated on the global heap.
> 
> If ptr1 points to a global-heap variable, and ptr2 points to a thread-local-heap variable, then you have to scan the global heap in order to collect the memory ptr2 is pointing to.

Until I learned that shared(x) doesn't actually work I didn't think this would be an issue.  Maybe it won't anyway?  With shared(int)* the type constructor behavior does kick in, right?  So you should have a local pointer to a shared int.  Classes are where this gets weird, and I guess you could have:

class C
{
    shared C ptr1;
}

shared C* x = &c.ptr1;

Pointers to handles are legal, right?

>>> Hm... this brings up an interesting point.  If you want to say that a
>> reference to a shared class is a thread-local, how do you do that?  For instance, in your struct A, you are most likely not going to share the actual reference lst (i.e. &a.lst), just what lst points to.  However, the compiler is going to assume you are sharing lst because that's what you declared.  So every access to lst is going to require special memory barriers (and technically wasted cycles).  Is there going to be a way around this?  With const it was not as important because const is a compile-time concept, but now the syntax deficiency is creeping into runtime and hurting performance.  I.e. are we going to get an equivalent "Rebindable" type constructor?
>> 
>> See my example of "shared (Something) p" above.  This is consistent with how const and such work anyway.
> 
> No it is not.  If you have const(T) x, you can never rebind x, no matter what type is inside the parens.

Lame.  Oh well.  With const it isn't as big an issue.
January 07, 2010
I think the per-thread gc thing will still work but it would require some compile-time magic to look for shared references.  Seems feasible if "new" were in the library.

Sent from my iPhone

On Jan 7, 2010, at 3:15 PM, Sean Kelly <sean at invisibleduck.org> wrote:

> On Jan 7, 2010, at 1:57 PM, Steve Schveighoffer wrote:
>>
>
>>> I'd think it would be pretty common to want a local reference to
>>> shared data.
>>> Then you wouldn't have to pay for the atomic read required if the
>>> reference
>>> itself is shared.
>>
>> I think more than const, we need something like this for shared.
>
> Yes.  If it can be done with a wrapper like Rebindable then great. But not having any way around this would really stink.
>
>>> Yeah, a fully shared field inside a non-shared class doesn't sound
>>> right.  The
>>> smoke test for me is that I think about whether it would work with
>>> per-thread
>>> GCs.  If one thread has a reference to "shared" data that actually
>>> lives in
>>> another thread's heap or stack, there's a dangling reference issue
>>> if the thread
>>> terminates (at least in theory--the heap data could easily be
>>> handed off to the
>>> shared GC instead, but let's pretend this is impossible).
>>
>> Yeah, allowing this seems to imply that the global heap will have to be scanned for local heap collections:
>>
>> class C
>> {
>>  shared(int)* ptr1;
>>  int *ptr2;
>> }
>>
>> I assume that marking a single member shared makes C get allocated on the global heap.
>>
>> If ptr1 points to a global-heap variable, and ptr2 points to a thread-local-heap variable, then you have to scan the global heap in order to collect the memory ptr2 is pointing to.
>
> Until I learned that shared(x) doesn't actually work I didn't think this would be an issue.  Maybe it won't anyway?  With shared(int)* the type constructor behavior does kick in, right?  So you should have a local pointer to a shared int.  Classes are where this gets weird, and I guess you could have:
>
> class C
> {
>    shared C ptr1;
> }
>
> shared C* x = &c.ptr1;
>
> Pointers to handles are legal, right?
>
>>>> Hm... this brings up an interesting point.  If you want to say that a
>>> reference to a shared class is a thread-local, how do you do
>>> that?  For
>>> instance, in your struct A, you are most likely not going to share
>>> the actual
>>> reference lst (i.e. &a.lst), just what lst points to.  However,
>>> the compiler is
>>> going to assume you are sharing lst because that's what you
>>> declared.  So every
>>> access to lst is going to require special memory barriers (and
>>> technically
>>> wasted cycles).  Is there going to be a way around this? With
>>> const it was not
>>> as important because const is a compile-time concept, but now the
>>> syntax
>>> deficiency is creeping into runtime and hurting performance.  I.e.
>>> are we going
>>> to get an equivalent "Rebindable" type constructor?
>>>
>>> See my example of "shared (Something) p" above.  This is
>>> consistent with how
>>> const and such work anyway.
>>
>> No it is not.  If you have const(T) x, you can never rebind x, no matter what type is inside the parens.
>
> Lame.  Oh well.  With const it isn't as big an issue.
> _______________________________________________
> dmd-concurrency mailing list
> dmd-concurrency at puremagic.com
> http://lists.puremagic.com/mailman/listinfo/dmd-concurrency
January 10, 2010
Also, adding a synchronized qualifier works only for variables, and not for other lvalues (like *p).

Andrei Alexandrescu wrote:
> Adding a "synchronized" qualifier (with the semantics of tail-shared) has been discussed before. Our interim conclusion was that there is enough information inside synchronized methods to dispense with that particular qualifier.
>
> We would very much like to _not_ add new qualifiers unless absolutely necessary. Your examples don't express that necessity - if you simply remove the "synchronized" qualifier off i and j, the compiler has enough information to do what's needed.
>
>
1 2 3
Next ›   Last »