Isolated by example

deadalnix via Digitalmars-d digitalmars-d at puremagic.com
Thu May 1 23:51:47 PDT 2014


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.
   // a.foo() // Error
   a = new A(); // OK
   a.foo(); // OK, we have written in a.
}

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.

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.

std.parallelism can also benefit from this. There is also several 
benefit when the optimizer knows about isolated (different island 
do not alias each other).

I hope the idea get across better with some sample code and will 
be considered. As sample code shows, isolated do not need to be 
specified explicitly often. User not annotating can get a lot of 
benefit out of the concept right away.


More information about the Digitalmars-d mailing list