www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Casting shared off safely

The following proposal exists to document a solution to the 
problem of safely casting shared off an object.
However, due to current politics that I oppose, removal of 
synchronized statements is expected to be removed from the 
language in the first edition and therefore this cannot be 
accepted.

First, a new storage class is added which I have dubbed 
``onstackonly``.
It has the primary feature of not allowing a given variable to be 
copied into another variable with an equal or longer lifetime.

```d
int* example1(onstackonly int* input) => input; // Error
```

It overloads:

```d
class Example2 {
     void method() {}
     void method() shared {}
     void method() onstackonly {}
}
```

The storage classes ``shared`` and ``onstackonly`` conflict, if 
both are on a variable ``shared`` wins.

```d
void example3(onstackonly int* input)  system {
     shared int* temp = cast(shared)input;
     // temp is not onstackonly
}
```

As a storage class, ``onstackonly`` is very similar to ``scope``, 
however its job isn't escape analysis, and perhaps ``scope`` 
should fallback to its behaviors if no other escape analysis is 
enabled.
It is allowed to implicitly cast to ``scope``.

```d
class Example4 {
     void method1() scope {
     }

     void method2() onstackonly {
     }

     void method2() scope {
     }
}

Example4 e = new Example4;

e.method1(); // ok
e.method2(); // ok, this is onstackonly
```

As a storage class, there is no way to put it on a variable in 
`` safe`` code directly.
How you do it, is by using a ``synchronized`` statement.

```d
synchronized(onstackonly self = this) {
}
```

For methods, if it is marked ``synchronized``, it is converted to 
``shared``, and it is automatically applied to the this pointer.

```d
void method5() synchronized { // is actually shared not 
synchronized
     onstackonly typeof(this) self = this; // ok because the this 
pointer is onstackonly
}
```

This enables a fairly simple approach that can be standardized to 
shared objects.
Giving the following life cycle:

1. Allocation with GC, owning reference. Can initialize.
2. Protected with a mutex (monitor) for inter-thread accessors.
3. Unprotected but still guarded, temporary ownership borrowing 
with the lock locked.

```d
class Example6 {
     void initMe() {
     }

     void unknownAccessor() shared {
     }

     void tempOwn() onstackonly {
     }
}

Example6 e1 = new Example6;
e1.initMe();

shared(Example6) e2 = cast(shared)e1;
e2.unknownAccessor();

synchronized(onstackonly Example6 e3 = e2) {
     e3.tempOwn();
}
```

Assuming we had custom root classes and D class monitor 
decoupled, we could define it as such:

```d
class SharableRootClass : void {
     final void opMonitorEnter(scope Sharable sharable) {
         // monitor.lock;
     }

     final void opMonitorExit(scope Sharable sharable) {
         // monitor.unlock;
     }

     shared(This) protect(this This)()              trusted => 
cast(shared)this;
     shared(This) protect(this This)() onstackonly  trusted => 
cast(shared)this;

     This consume(this This)()         onstackonly  system => this;
     This consume(this This)()         shared       system => this;
}
```

By doing the custom root class and D class monitor decoupling, we 
can now make the default root class of a given edition not have a 
monitor, but still support it for the use cases where it is the 
right tool for the job.
Jan 18