The lifecycle is bounded by the duration of the Runnable passed to the `run()` method.

In the simple case, with no inheritance and no snapshotting:

    ScopedLocal.where(x, xExpr).run(r)

the lifecycle of the binding starts when we enter r, and ends when we return from r.

With snapshotting, the answer is the same, just less obviously so.  If I take a snapshot of a set of variables, we're not in a scope yet; we'll enter a new scope when we use that snapshot to run a task, possibly multiple times.

With inheritance, it is possible that the child thread can live longer than the lifetime of the task that spawned it; this is something we hope we can avoid through _structured concurrency_ -- that the inheritance is restricted to cases where we can prove the lifetime of the parent is longer than the lifetime of the child.  There's more work to be done here.

To be explicit, the two assumptions we need in order to avoid people shooting their feet are:

 - ScopeLocal values are effectively final "all the way down". We can't enforce this, but if users had to synchronize when accessing the state of scoped locals, then someone made a big mistake.  (This joins a very long list of mutability bugs we can't prevent but for which we have to communicate to users what they shouldn't be doing, not unlike "don't modify the source of the stream while traversing it.")

 - When inheriting, the parent must outlive the child.

When these assumptions hold, understanding the lifecycle is trivial.



On 5/14/2021 3:19 PM, Mike Rettig wrote:
I don't see a way to capture the lifecycle of the scoped variable. I think
for framework designers it's going to be important to know the life cycle
of the scoped variable especially with snapshotting and inheritance.  The
lack of a defined life cycle for thread locals is one of the many reasons
why it should be avoided. If scoped locals have a well defined and
comprehensive life cycle then it will make working with them much easier
(especially when many threads are involved).

interface ScopedVariableLifecycle<T> {
    ScopedVariable<T> begin();
}

interface ScopedVariable<T> {
    T get();
    void end();

    Optional<ScopedVariableLifecycle<T>> createForChildScope();
    Optional<ScopedVariableLifecycle<T>> createSnapshot();
}

static final ScopeLocal<MyType> x = ScopeLocal.forType(MyType.class);

//most scoped variables will probably use the standard singleton
implementations.

ScopedLocal.where(x, new Singleton(new MyType()));

ScopedLocal.where(x, new InheritableSingleton(new MyType()));


//a custom lifecycle (e.g. database connection that is opened on first
access and closed at the end of the scope).

ScopedLocal.where(x, new MyScopedVariableLifecycle());


On Wed, May 12, 2021 at 10:15 AM Andrew Haley <a...@redhat.com> wrote:

There's been considerable discussion about scope locals on the loom-dev
list,
and it's now time to open this to a wider audience. This subject is
important
because. although scope locals were motivated by the the needs of Loom,
they
have many potential applications outside that project.

The draft JEP is at

https://bugs.openjdk.java.net/browse/JDK-8263012

I've already received some very helpful suggestions for enhancements to
the API, and it'll take me a while to work through them all. In particular,
Paul Sandoz has suggested that I unify the classes Snapshot and Carrier,
and it will take me some time to understand the consequences of that.

In the meantime, please have a look at the JEP and comment here.


For reference, earlier discussions are at

https://mail.openjdk.java.net/pipermail/loom-dev/2021-March/002268.html
https://mail.openjdk.java.net/pipermail/loom-dev/2021-April/002287.html
https://mail.openjdk.java.net/pipermail/loom-dev/2021-May/002427.html

--
Andrew Haley  (he/him)
Java Platform Lead Engineer
Red Hat UK Ltd. <https://www.redhat.com>
https://keybase.io/andrewhaley
EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671




Reply via email to