[dmd-concurrency] tail-shared by default?

Steve Schveighoffer schveiguy at yahoo.com
Fri Jan 8 12:47:50 PST 2010


----- Original Message ----

> From: Walter Bright <walter at digitalmars.com>
> 
> Steve Schveighoffer wrote:
> > 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.
> 
> That's the so-called "tail-shared" problem. We tried (hard) to make "tail-const" 
> work, where the reference is mutable but the contents were const. It sounds 
> simple, but just does not work in a language with implicit reference semantics.

Yes, but this isn't const, it is shared.  You sometimes want const members or const stack variables.  I think you never want the equivalent shared items.

Let's think about how often you actually need to use shared as a storage class:

1) as a global/static variable
2) stack variable? never
3) member variabe? debatable -- you can always create a shared reference to the entire aggregate which seems more logical to me anyway.

What if we make shared *default* to tail-shared for reference and pointer types except for global variables (which will be fully shared), and make it illegal to declare a shared value type on the stack or as a member.

Basically, shared for a global variable means it is a globally shared value, but shared anywhere else means it references either a globally shared value or shared heap data.  Any other usage is illegal.

This makes it impossible to share stack data -- good.
This makes it impossible to have shared and local data in the same heap memory block -- good for thread-local heaps.
This makes it easy to declare a local reference to a shared class -- excellent.
This allows having a thread-local heap -- good.
This makes it neigh impossible to declare a thread-local global or static reference to a shared class -- bad for completeness, but is this anticipated to be a common usage?
This means if you want to share delegates, you have to mark the function as a shared closure, and it will always be heap allocated -- an acceptable tradeoff IMO.

examples:

shared int x; // ok, visible to all threads
shared C c; // ok, visible to all threads, the reference itself is shared.

struct S
{
}

class C
{
   shared int x; // illegal
   shared int *y; // legal, the pointer itself isn't shared -- equivalent to shared(int)* x
   shared int[] a; // legal, the array struct itself isn't shared -- equivalent to shared(int)[] a
   shared C c; // legal, the reference itself isn't shared
   shared S s; // illegal, same as shared int, it's a value type
}

void foo()
{
   shared int x; // illegal
   shared S s; // illegal
   shared C c; // legal, the reference itself isn't shared.
}

void fooShared(shared int* x, immutable int *y) // can only have shareable reference parameters
{
    int fn() shared // flags that the entire fooShared function will always be heap-allocated, even if passed to a scope handler.
    {
        return *x;
    }
}

What do you think about this?

-Steve



      


More information about the dmd-concurrency mailing list