[dmd-concurrency] Smoke test

Steve Schveighoffer schveiguy at yahoo.com
Fri Jan 8 04:44:30 PST 2010


----- Original Message ----

> From: Sean Kelly <sean at invisibleduck.org>
> To: Discuss the concurrency model(s) for D <dmd-concurrency at puremagic.com>
> Sent: Fri, January 8, 2010 3:18:12 AM
> Subject: Re: [dmd-concurrency] Smoke test
> 
> On Jan 7, 2010, at 5:28 PM, Walter Bright wrote:
> > 
> > Having a per-thread gc is an optimization, not a fundamental feature of the 
> concurrency model. For one thing, it precludes casting data to immutable. For 
> another, it may result in excessive memory consumption as one thread may have a 
> lot of unused data in its pool that is not available for allocation by another 
> thread.
> 
> I agree completely that having a per-thread GC is simply an optimization.  I 
> just brought it up because it's the simplest way I've come up with to think 
> about what "shared" means.  Put another way, I think of shared as controlling 
> the access control pool the data lives in, and the lifetime of that data.  If I 
> see "T v1" then I should be able to infer that v1 is only visible to the current 
> thread and will go away when the thread terminates.  Similarly, if I see "shared 
> T v2" I should be able to infer that v2 is globally visible and may remain until 
> process termination.
> 
> I feel like I'm not explaining myself very well, but that's the best I can do at 
> the moment.  As a related issue, I have a feeling that the following is a bad 
> idea, but I haven't come up with a good explanation for why yet, maybe simply 
> the principle of least surprise?:
> 
> class C
> {
>     shared int x;
> }
> 
> auto c = new C;
> sendRefToAnotherThread( c ); // fails, c is local
> sendToAnotherThread( &c.x ); // succeeds, c.x is shared

For one, it prevents thread-local heaps from being truly thread-local.  For example, in order for this class to be allocated, it must be allocated in the global heap for part of it to be shared.  But c would seem that it should be allocated locally.

A better example is this:

class B
{
}

class C
{
    shared int x;
    B b;
}

C c;
void foo()
{
   c = new C;
   sendToAnotherThread(&c.x)
   c.b = new B;
}

So, now you go to do a thread-local garbage collection.  How does it make the round-trip through the global heap to prevent collection of c.b, since c is allocated in the shared heap?  You'd have to do a mark-sweep on the global heap also.  This means a collection can't truly be thread local, and will be synchronized with all other thread collection cycles.

On the other hand, I can see uses for this, such as if x is some kind of flag or something that you want other threads to be able to read or set.  However, I think it should be fine to require that any memory block be all shared or all local, but no mixed.  In this case, you could implement the same thing with an extra allocation:

class C
{
   private shared(int) *_x;
   B b;
   this() {_x = new shared(int);} // _x is placed on global heap
   @property ref shared(int) x() {return *_x;}
}

(Forgive me for not knowing the required semantics of shared, assume the uses are properly annotated here.)

The bottom line is, we could have a rule that any members of a class or struct could have *references* to shared memory, but could not *contain* shared memory.  I think this is a reasonable rule.  It definitely would sit better with me and be easier to implement/explain.

The only caveat is, we would *need* to change the semantics of shared so that you can have a non-shared reference to a shared class instance easily.  I don't know if a wrapper struct would cut it because the compiler would enforce the same rules on the wrapper struct (maybe you mark it as @system?).  I have a feeling that you almost always want a thread-local reference to a shared class anyway (the exception being a global shared variable).

-Steve



      


More information about the dmd-concurrency mailing list