D2 Multithreading Architecture
Robert Jacques
sandford at jhu.edu
Thu Apr 30 07:58:15 PDT 2009
On Thu, 30 Apr 2009 07:04:59 -0400, Michel Fortin
<michel.fortin at michelf.com> wrote:
> On 2009-04-29 22:54:20 -0400, "Robert Jacques" <sandford at jhu.edu> said:
>
>> On Wed, 29 Apr 2009 21:25:32 -0400, Michel Fortin
>> <michel.fortin at michelf.com> wrote:
>>
>>> That's basically why I suggested adding scope constrains back then. To
>>> implement swap safely, you need to know that the scope of the pointer
>>> you are assigning to is always smaller or equal to the scope of the
>>> memory block you're feeding them with.
>>> Here's a new syntax for expressing contrains I've been thinking about:
>>> void swap(scope int* x, scope int* y)
>>> scope(x = y && y = x) // caller enforces that y is assignable to x
>>> and x to y
>>> {
>>> scope(x = t && t = y) int* t;
>>> // y assignable to t and t to x; also imply that
>>> // x is assignable to y, which holds against previous constrains
>>> t = y; // valid since scope(t = y)
>>> y = x; // valid since scope(y = x)
>>> x = t; // valid since scope(x = t)
>>> }
>>> Perhaps with simple escape analysis, the compiler could infer the
>>> scope constrains of local variable t so you don't have to write it
>>> everywhere.
>> You know, the implementation of swap is really a bad example, since
>> using a template works fine:
>> void swap(T)(ref T x, ref T y) {
>> T t
>> t = y;
>> y = x;
>> x = t;
>> }
>
> You know, all functions could be made templates and it'd solve all our
> problems. Why aren't we doing that?
>
> Seriously, templates can't be the answer to everything. What if you
> wanted swap as a member function of a class, and want to override it in
> a derived class? We need a system that works with non-templates too.
I agree, which is why using a function where people immediately think
'shouldn't that be a template' is a bad example.
By the way, using
scope(x = t && t = y) int* t;
implies to me that the function is templated.
>> Object a;
>> Object b;
>> shared Object c;
>> swap(a,b); // Okay
>> swap(b,c); // Error, template instantiation swap(local object, shared
>> object)
>
> In the case of my function with constrains you'd get something alike:
>
> swap(b,c); // Error, 'swap' wants c (shared Object c)
> // to be assignable to b (local Object b) which isn't allowed.
>
> Basically, you can't copy a scope variable to another scope variable
> inside a function (because they may not be of the same scope and/or
> ownership) unless the signature includes a constrain signaling the
> assignement, which is then evaluated at the call site.
Agreed
>> Here are some specific issues:
>> 1) You seem to assume that different ownerships are interchangable.
>> They are not. Even if the data layout and member signatures are the
>> made to be the same, shared objects must maintain sequential
>> consistency (i.e. memory fences).
>> 1a) Limiting object signatures to being identical makes it hard for
>> library writers to make a class that can be both allocated on both the
>> shared and local heaps.
>
> Where am I assuming that? How?
Sorry. I was making assumptions based on your previous proposal of scope
constraints:
void swap(scope ref int* a, scope ref int* b)
if ((*a).scope <= b.scope && (*b).scope <= a.scope)
The <= operator implies that it is possible that an object of one
ownership might get assigned to an object of a different ownership.
I was also making the assumption that the scope function parameter implied
templating on that ownership type, as implied by.
scope(x = t && t = y) int* t;
Speaking of limiting constraints to equality, how about:
void swap(scope[0] ref int* x, scope[0] ref int* y) {
scope[0] t;
t = x;
x = y;
y = t;
}
with scope meaning any owner and scope[id] meaning the same ownertype as
all the other scope[id]'s. (id is a number or identifier)
Hmm... scope[>id] and scope[<id] also seem intuitive.
An advantage I see with this approach is that swap composes much more
easily:
void bar(scope ref int* x, scope ref int* y)
scope(x = y && y = x) // Possible information loss, compiler knows
satisfaction, not what generated satisfaction
{
swap(x,y); // Does the compiler know enough to allow this
call?
}
void bar(scope[0] ref int* x, scope[0] ref int* y) {
swap(x,y); // Yes, the compiler knows x and y have the same scope
}
> Perhaps I'm not understanding something of your proposal, but I don't
> see how adding scope constrains breaks shared objects.
Adding any scope restraints other than equality, can be shared objects.
See above.
Perhaps this will explain my thinking better:
shared class Foo {}
becomes
Interface __scope_Foo {}
class __local_foo: __scope_Foo {}
class __shared_foo: __scope_Foo {}
under the hood. Even if scope is a super-type and not an interface, you
can see how these two ownerships might produce incompatible classes. Of
importance is that member variables are sequentially consistent on shared
types vs non-shared types and that they may have different vtables, etc.
> You say you want "scope" to be the super-type of all, which means that
> it should accept both local and shared variables, am I right? If that's
> the case I was right to write variable as being scope in my swap
> function, so it can accept everything.
No, I said that scope is the super-interface of all. Which would have made
a difference if you were using objects instead of int*s.
>> 2) You shouldn't rely on escape analysis to determine your function
>> signature. It essentially forces you to do whole program static escape
>> analysis, if you want to do it right, which is implausible. Consider
>> recursive and member functions. What's the proper signature? And this
>> isn't even considering the composability and forward referencing issues.
>
> That I certainly agree with. (And I never suggested escape analysis to
> determine the scope of function arguments, only local variables inside
> the function.)
Opps, I misread that.
More information about the Digitalmars-d
mailing list