Isolated by example

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Fri May 2 06:39:15 PDT 2014


On Fri, 02 May 2014 02:51:47 -0400, deadalnix <deadalnix at gmail.com> wrote:

> First the original post I made on this forum :  
> http://forum.dlang.org/thread/kluaojijixhwigoujeip@forum.dlang.org#post-kluaojijixhwigoujeip:40forum.dlang.org
>
> Now some sample code/explanation to get it better.
>
> Isolated in a proposal adapted from an experiment made in C# for D. It  
> introduces a new qualifier. The qualifier is necessary on function  
> signatures (inference is possible to some extent, like for pure  
> functions), object/struct fields and globals. It is inferred on local  
> variables (but can be explicited if one wish).
>
> An isolated is in an 'island'. The 'island' is implicit and tracked by  
> the compiler. We have one immutable and one shared island. We have one  
> TL island per thread. And we can have an infinity of isolated island.
>
> An isolated is consumed when:
>   - it is returned
>   - it is passed as argument
>   - it is assigned to another island
>
> When an isolated goes out of scope without being consumed, the compiler  
> can free the whole island:
>
> void foo() {
>    A a = new A(); // a is isolated if A's ctor allows.
>
>    // a is not consumed. The compiler can insert memory freeing.
> }
>
> As we can see, it allows the compiler to do some freeing for us to  
> reduce GC pressure. Manu why aren't you already here cheering ?
>
> When an isolated is consumed, the island it is in is merged into the  
> island that consumes it. All reference to the island become write only  
> until next write.
>
> void foo() {
>    A a = new A(); // a is isolated
>    immutable b = a; // a's island is merged into immutable
>
>    // a's island has been consumed. a is not readable at this point.

When you say "consumed", you mean it statically cannot be used, not that  
it was set to null or something, right?

>    // a.foo() // Error
>    a = new A(); // OK
>    a.foo(); // OK, we have written in a.

OK, but what happens if I do this?

      auto c = a;
      a.foo();

At this point, a was inferred typed as isolated, but in this section, it  
doesn't have to be. Will the type change in different parts of the  
function? Or will this simply be statically disallowed?

> }

>
> So far, we saw that isolated helps to construct const/immutable/shared  
> objects and can be used by the compiler to insert free.
>
> isolated also help to bridge the RC world and the GC world.
>
> struct RC(T) is(isReferenceType!T) {
>    private T mystuff;
>
>    this(isolated T stuff) {
>      mystuff = stuff;
>    }
>
>    // All code to do ref counting goes here...
> }
>
> Here, the RC struct must be constructed with an isolated. To do so, the  
> isolated have to be passed as argument: it is consumed. As a result, the  
> RC struct can be sure that it has the only usable reference to stuff  
> that is around.

mystuff should be marked isolated too, no?

> Now some concurrency goodies:
>
> void foo() {
>    auto tid = spawn(&spawnedFunc, thisTid);
>
>    A a = new A();
>    send(tid, a); // OK, a is an isolated.
>
>    // a can't be used here anymore as it is consumed.
>    // You can simply pass isolated around across thread safely.
> }
>
> Now that is pretty sweet. First we don't have to do the crazy and unsafe  
> dance of "cast to shared and cast back to me" and this is actually safe.  
> Go's type system do is not safe across channel, that puts us ahead in  
> one of the things go does best.

Yes, I really like this idea. This is what is missing from the type system.

I read in your previous post that shared can benefit from using isolated,  
by not having to lock all sub-objects. But I'm confused as to how that  
would work. If a variable is shared, by default, it can be passed around.  
But wouldn't the passing of the shared variable mean you have to disallow  
access to the isolated member? The compiler would have to be aware of the  
locking protection and enforce it.

Essentially, you could access a locked isolated variable, but not a shared  
isolated variable. These kinds of requirements need to be specified  
somehow.

Also, when you call a method, how does the island get handled?

abstract class A
{
    void foo(ref A other) { other = this;}
    void bar(int n);
}

class B
{
    private isolated int x;
    void bar(int n) { x = n; } // I'm assuming this is ok, right?
}

auto b = new B;
A a;
b.foo(a);

At this point, both b and a refer to the same object. How does the  
compiler know what to prevent here? At what point does the island become  
inaccessible?

Another issue is with delegates. They have no reliable type for the  
context pointer.

-Steve


More information about the Digitalmars-d mailing list