On 06/08/12 06:03, Steven Schveighoffer wrote:
> On Thu, 07 Jun 2012 20:58:13 -0400, Artur Skawina <[email protected]> wrote:
>
>> On 06/08/12 01:51, Steven Schveighoffer wrote:
>>>
>>> The following would be illegal:
>>>
>>> struct X
>>> {
>>> shared int x; // illegal
>>> shared(int)* y; // legal
>>>
>>> shared(X) *next; // legal
>>> }
>>
>> Note that the type of 'x' in
>>
>> shared struct S {
>> int x;
>> }
>>
>> should probably be 'shared(int)'.
>> Which lets you safely take an address of an aggregates field.
>
> That's one of the things I'm wondering about. Should it be allowed?
Must be. We're talking about the next generation of a high level assembler,
not logo. :)
>
> I agree that the type should be shared(int), but the type should not transfer
> to function calls or auto, it should be sticky to the particular variable.
> Only references should be sticky typed.
The problem with this is that it should be symmetrical, IOW the conversion
from non-shared to shared would also have to be (implicitly) allowed.
A type that converts to both would be better, even if harder to implement.
>> And I'm not sure if marking a struct and class as shared would work
>> correctly right now, it's probably too easy to lose the 'shared' qualifier.
>
> Right, I was thinking shared structs do not make sense, since I don't think
> shared members do not make sense. Either a whole struct/class is shared or
> it is not. Because you can only put classes on the heap, shared makes sense
> as an attribute for a class.
>
> But then again, it might make sense to say "this struct is only ever shared,
> so it should be required to go on the heap". I like your idea later about
> identifying shared struct types that should use synchronization.
Of course shared structs make sense, it's what allows implementing any
non-trivial shared type.
static Atomic!int counter;
inside a function is perfectly fine. And, as somebody already mentioned
in this thread, omitting 'static' should cause a build failure; right
now it is accepted, even when written as
shared Atomic!int counter;
The problem? 'shared' is silently dropped. Move the counter from a struct
into a function after realizing it's only accessed from one place, forget
to add 'static' - and the result will compile w/o even a warning.
>
>>> If you notice, I never allow shared values to be stored on the stack, they
>>> are always going to be stored on the heap. We can use this to our
>>> advantage -- using special allocators that are specific to shared data, we
>>> can ensure the synchronization tools necessary to protect this data gets
>>> allocated on the heap along side it. I'm not sure exactly how this could
>>> work, but I was thinking, instead of allocating a monitor based on the
>>> *type* (i.e. a class), you allocate it based on whether it's *shared* or
>>> not. Since I can never create a shared struct X on the stack, it must be
>>> in the heap, so...
>>>
>>> struct X
>>> {
>>> int y;
>>> }
>>>
>>> shared(X) *x = new shared(X);
>>>
>>> synchronized(x) // scope-locks hidden allocated monitor object
>>> {
>>> x.y = 5;
>>> }
>>>
>>> x.y = 5; // should we disallow this, or maybe even auto-lock x?
>>>
>>> Hm... another idea -- you can't extract any piece of an aggregate. That
>>> is, it would be illegal to do:
>>>
>>> shared(int)* myYptr = &x.y;
>>>
>>> because that would work around the synchronization.
>>
>> That's too restrictive. It would overload 'shared' even more. If you
>> want that kind of synchronize magic to work, just allow:
>>
>> shared synchronized(optional_locking_primitive) struct S {
>> ...
>> }
>>
>> And *now* 'x.y = 5' can do its magic, while '&x.y' can be disallowed.
>>
>>
>> shared struct S {
>> Atomic!int i;
>> }
>> shared(S)* p = ...
>> p.i += 1;
>>
>> should work, so accessing fields must remain possible.
>
> OK. You are right, synchronized may be overkill for basic types.
>
>>> auto would have to strip shared:
>>>
>>> auto myY = x.y; // typeof(myY) == int.
>>
>> Hmm, i'm not sure about this, maybe it should be disallowed; it could
>> only strip 'shared' from the head anyway.
>
> Yes, head stripping. I think it should be allowed. For instance, if you
> wanted to read a shared double, and take the cosine of it, this should be
> allowed:
>
> auto n = cos(sharedValue);
>
> If it's not, the alternatives are:
>
> auto n = cos(cast()sharedValue);
>
> or
>
> double v = sharedValue; // explicit removal of shared
> auto m = cos(v);
>
> Neither of these look necessary, I think just allowing shared value types to
> automatically convert to non-shared versions works the best.
If 'shared(VT)' implicitly converts to VT, then
auto myY = x.y; // typeof(myY) == shared(int)
would still be fine. So would
auto n = cos(x,y); // assuming some weird cos() that works on ints ;)
But I'm not sure allowing these implicit conversions is a good idea.
At least not yet. :)
>>> This is definitely not a complete proposal. But I wonder if this is the
>>> right direction?
>>
>> I think it is.
>
> good.
It's a small step in the right direction.
artur