Re: JEP draft: Scope Locals

2021-05-20 Thread Andrew Haley
On 5/19/21 10:51 PM, David Lloyd wrote:
> I think it would be really nice if the snapshot class could hold its
> run/call method rather than making it a static method of `ScopeLocal`. This
> reads more naturally to me (and is nicer to write):

True, but inheritance is *extremely* time-critical when creating a
ForkJoin task. In many cases there won't be any scope locals to inherit,
and by allowing null snapshots you save a significant fraction of a
nanosecond when creating one. And I know, this is hard to believe, but
such an overhead has a significant macroscopic effect on benchmarks.

However, I do intend to fix this in a different way, if I can.

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



Re: JEP draft: Scope Locals

2021-05-20 Thread Andrew Haley
On 5/19/21 9:55 PM, David Lloyd wrote:
> Turning this around, I would argue that there are few (or perhaps *no*)
> cases where it would ever be desirable to inherit scope locals across
> thread creation; in cases where this is explicitly desired, one can always
> resume the snapshot from within the thread's Runnable. Was there a
> particular use case this was meant to address?

Structured Concurrency, but also possibly anywhere that inheritable
thread locals are used now.

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



Re: JEP draft: Scope Locals

2021-05-20 Thread Andrew Haley
On 5/19/21 5:55 PM, Peter Levart wrote:
> In other words, non-inheritable bindings are 
> never transferred from thread to thread automatically or by 
> snapshot/runWithSnapshot. I can see that snapshot/runWithSnapshot was 
> meant as a mechanism to "simulate" inheritance of bindings when 
> execution is transferred from one thread to another which is not a newly 
> started child thread.

Yes. However, this part of the draft proposal is undergoing some revision,
and it looks like it will make more sense to control inheritance in a
different way, one that will permit more flexible control over what
gets inherited and when.

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



Re: JEP draft: Scope Locals

2021-05-20 Thread Andrew Haley
On 5/15/21 6:15 PM, Remi Forax wrote:
> I think the JEP should be more explicit about the shortcoming of ThreadLocal 
> and how the design of scope local fix both issues.

Yes. It's in progress.

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



Re: JEP draft: Scope Locals

2021-05-19 Thread David Lloyd
On Wed, May 12, 2021 at 9:43 AM Andrew Haley  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.
>

I didn't get a chance to mention earlier, but I think scope locals are
shaping up really great so far: simple yet powerful.  I know this will be a
really useful addition to the JDK.  There are several projects both within
and without Red Hat that I have authored or have contributed to which use a
very compatible pattern (that is, using a lexically constrained binding
strategy for context association) and are perfect candidates to switch over
to use this feature.

That said, I have one minor comment about the API. :-)

I think it would be really nice if the snapshot class could hold its
run/call method rather than making it a static method of `ScopeLocal`. This
reads more naturally to me (and is nicer to write):

var snap = ScopeLocal.snapshot();
return executor.submit(() -> snap.call(aCallable));

Than this:

var snap = ScopeLocal.snapshot();
return executor.submit(() -> ScopeLocal.callWithSnapshot(aCallable, snap
));

This kind of lexically bound contextual pattern can be found in several of
our existing projects (including those listed below [1] [2]).  In writing
these projects, we took it a step further than just Runnable and Callable
and added variants which accept `Consumer` plus a `T` to pass in, a
`Function` plus a `T` to pass in, and `BiConsumer` and `BiFunction`
variants as well which accept two arguments to pass in.  The practical
reason here is that we could then pass in a method handle with explicit
arguments rather than relying on lambda capture, which can be expensive in
certain circumstances.  The overall benefit vs cost of doing this is
probably debatable, but I thought the idea might be interesting at any rate.

Anyway that's it.  I love this feature and am excited to see it get into
the JDK!

[1] WildFly Elytron "Scoped" API, used by client and server authentication
contexts (WildFly's version of JAAS Subject):
https://github.com/wildfly-security/wildfly-elytron/blob/1.x/auth/server/base/src/main/java/org/wildfly/security/auth/server/Scoped.java
[2] WildFly Common "Contextual" API used for transaction, configuration,
and other context propagation:
https://github.com/wildfly/wildfly-common/blob/master/src/main/java/org/wildfly/common/context/Contextual.java

-- 
- DML • he/him


Re: JEP draft: Scope Locals

2021-05-19 Thread David Lloyd
On Wed, May 19, 2021 at 4:01 AM Andrew Haley  wrote:

> On 5/15/21 6:50 PM, Peter Levart wrote:
> > What if I wanted to create and start a thread that would be "pristine" -
> > not have any ScopeLocal value bound? Is this intentionally not allowed
> > in order to make sure that inheritable ScopeLocal(s) can't be cleared by
> > code that has no access to ScopeLocal instance(s)?
>
> That one is about to be changed by a revision to the JEP. There clearly
> is a need to control whether a newly-created thread inherits scope locals
> or not. For instance, an Executor might lazily create worker threads, and
> we don't want them to inherit scope locals from the thread that was
> running.
>

Turning this around, I would argue that there are few (or perhaps *no*)
cases where it would ever be desirable to inherit scope locals across
thread creation; in cases where this is explicitly desired, one can always
resume the snapshot from within the thread's Runnable. Was there a
particular use case this was meant to address?

-- 
- DML • he/him


Re: JEP draft: Scope Locals

2021-05-19 Thread Peter Levart



On 15/05/2021 19:50, Peter Levart wrote:
Another question: Suppose there are two inheritable ScopeLocal 
variables with bound values in scope (A and B) when I take a snapshot:


var snapshot = ScopeLocal.snapshot();

now I pass that snapshot to a thread which does the following:

ScopeLocal
    .where(C, "value for C")
    .run(() -> {
        System.out.printf("A:%s B:%s C:%s\n", A.isBound(), 
B.isBound(), C.isBound());

        ScopeLocal.runWithSnapshot​(() -> {
            System.out.printf("A:%s B:%s C:%s\n", A.isBound(), 
B.isBound(), C.isBound());

        }, snapshot);
        System.out.printf("A:%s B:%s C:%s\n", A.isBound(), 
B.isBound(), C.isBound());

    });

What would this code print?

...in other words, does runWithSnapshot replace the whole set of bound 
values or does it merge it with existing set? 



...let me answer this myself, after checking current implementation. The 
answer is: "It depends on whether C is an inheritable ScopeLocal or 
non-inheritable". If I understand the code correctly there are two 
independent sets of bindings: inheritable and non-inheritable. 
snapshot() retrieves the current inheritable set and runWithSnapshot​() 
replaces current inheriatable set with the snapshot for the execution of 
given Runnable and afterwards swaps previous inheritable set back.


So if C is inheritable, the output would be:

A:false B:false C:true
A:true B:true C:false
A:false B:false C:true

...but if C is non-inheritable, the output would be:

A:false B:false C:true
A:true B:true C:true
A:false B:false C:true

This seems consistent. In other words, non-inheritable bindings are 
never transferred from thread to thread automatically or by 
snapshot/runWithSnapshot. I can see that snapshot/runWithSnapshot was 
meant as a mechanism to "simulate" inheritance of bindings when 
execution is transferred from one thread to another which is not a newly 
started child thread.


Regards, Peter

Peter



Re: [External] : Re: JEP draft: Scope Locals

2021-05-19 Thread Alan Bateman

On 19/05/2021 10:15, Andrew Haley wrote:

:
Yes, that's true. I think that what you have in mind is a more elaborate
mechanism than what is proposed here, which does no more than bind values
to names over a scope. There needs to be more discussion in the JEP of
what this proposal isn't intended to do, and how we might cope with
mutability of a scope local's values.

Also just to add that the closable-resource in the example is being used 
on concurrent threads so it already has to cope with attempted usage 
after close or async close (close while in use). Also once we combine 
this with SC then we can be sure that the child task has completed 
before closing the resource.


-Alan


Re: [External] : Re: JEP draft: Scope Locals

2021-05-19 Thread Andrew Haley
On 5/18/21 3:19 AM, Mike Rettig wrote:
> With the current proposal I can't simply flush and close at the
> end of the scope because there might be a child scope that is still active.

Yes, that's true. I think that what you have in mind is a more elaborate
mechanism than what is proposed here, which does no more than bind values
to names over a scope. There needs to be more discussion in the JEP of
what this proposal isn't intended to do, and how we might cope with
mutability of a scope local's values.

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



Re: JEP draft: Scope Locals

2021-05-19 Thread Andrew Haley
On 5/15/21 6:50 PM, Peter Levart wrote:
> What if I wanted to create and start a thread that would be "pristine" - 
> not have any ScopeLocal value bound? Is this intentionally not allowed 
> in order to make sure that inheritable ScopeLocal(s) can't be cleared by 
> code that has no access to ScopeLocal instance(s)?

That one is about to be changed by a revision to the JEP. There clearly
is a need to control whether a newly-created thread inherits scope locals
or not. For instance, an Executor might lazily create worker threads, and
we don't want them to inherit scope locals from the thread that was
running.

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



Re: [External] : Re: JEP draft: Scope Locals

2021-05-17 Thread Brian Goetz




Let's try again: why is that important?   What decisions would you
make differently if you had this information?  What benefit would
come from those decisions?

Threading is hard.


Gee, thanks MIke for pointing that out to me, obviously I'm new to the 
topic :)


Scoped variables are also hard to get right. Combining the two without 
an explicit api ignores the complexity that will arise as scoped 
variables are passed to other threads.


OK, now we're getting to the actual question; you obviously have some 
assumptions about the degree of sharing expected, that might be skipping 
a few steps (and then skipping some more steps by jumping to a 
solution).  So to ask again: what patterns of access / sharing are you 
assuming, what risks are you anticipating, etc?




Re: [External] : Re: JEP draft: Scope Locals

2021-05-17 Thread Brian Goetz




Let's back up a lot of steps; you're deep in proposing a solution
when you've not even explained what problem you think you're
solving.  So control question, which I hope will start to expose
the assumptions:

   Why do you think its important to know that a snapshot of a
variable has occurred?


From the JEP:
"In addition, a |Snapshot()| operation that captures the current set 
of inheritable scope locals is provided. This allows context 
information to be shared with asynchronous computations."


As the provider of the scoped variable, I'd certainly like to know 
that my scoped variable is being passed into a new scope.


Let's try again: why is that important?   What decisions would you make 
differently if you had this information?  What benefit would come from 
those decisions?




Re: [External] : Re: JEP draft: Scope Locals

2021-05-17 Thread Brian Goetz




How so? How do I know if a snapshot of my variable has occurred?


Let's back up a lot of steps; you're deep in proposing a solution when 
you've not even explained what problem you think you're solving.  So 
control question, which I hope will start to expose the assumptions:


   Why do you think its important to know that a snapshot of a variable 
has occurred?





Re: JEP draft: Scope Locals

2021-05-15 Thread Peter Levart

Hi,

So if scopeLocal.get() is called in a thread outside any .run() scope in 
that thread, it will throw exception and scopeLocal.isBound() will 
return false. Unless it was called on an inheritableScopeLocal which 
inherited binding from parent thread.


What if I wanted to create and start a thread that would be "pristine" - 
not have any ScopeLocal value bound? Is this intentionally not allowed 
in order to make sure that inheritable ScopeLocal(s) can't be cleared by 
code that has no access to ScopeLocal instance(s)?


Another question: Suppose there are two inheritable ScopeLocal variables 
with bound values in scope (A and B) when I take a snapshot:


var snapshot = ScopeLocal.snapshot();

now I pass that snapshot to a thread which does the following:

ScopeLocal
    .where(C, "value for C")
    .run(() -> {
        System.out.printf("A:%s B:%s C:%s\n", A.isBound(), B.isBound(), 
C.isBound());

        ScopeLocal.runWithSnapshot​(() -> {
            System.out.printf("A:%s B:%s C:%s\n", A.isBound(), 
B.isBound(), C.isBound());

        }, snapshot);
        System.out.printf("A:%s B:%s C:%s\n", A.isBound(), B.isBound(), 
C.isBound());

    });

What would this code print?

...in other words, does runWithSnapshot replace the whole set of bound 
values or does it merge it with existing set?



Peter

On 5/12/21 4:42 PM, Andrew Haley 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



Re: JEP draft: Scope Locals

2021-05-15 Thread Remi Forax
Apart the performance, threadLocal has currently two issues that are addressed 
by scope locals.

ThreadLocal let the programmer defines the lifecycle, while it seems great on 
paper, it's a huge mess in reality.
Because not everybody wraps the calls to set()/remove() in a try/finally, so 
the values inside thread locals are not reclaimed by the GC.
It was less an issue before pooling because all the values are reclaimed when 
the thread die, but with pooling, threads do not die.
(I think this is the point made by Brian about ThreadLocal not interacting 
smoothly with ThreadLocal)

InheritableThreadLocal copies everything each time a new thread is created, 
again this lead to memory leaks because
if an unexpected threads is created, sadly some libraries do that, all values 
are now referenced by this new thread that may never die.

If you want more
https://www.google.com/search?hl=en&q=ThreadLocal%20memory%20leak

I think the JEP should be more explicit about the shortcoming of ThreadLocal 
and how the design of scope local fix both issues.

Rémi

- Mail original -
> De: "Brian Goetz" 
> À: "Andrew Haley" , "core-libs-dev" 
> , "loom-dev"
> 
> Envoyé: Mercredi 12 Mai 2021 20:57:33
> Objet: Re: JEP draft: Scope Locals

> Scope locals have come together nicely.
> 
> I have some vague thoughts on the presentation of the JEP draft.  There
> are (at least) three intertwined things in the motivation:
> 
>  - ThreadLocal (and ITL especially) were always compromises, and with
> the advent of Loom, have become untenable -- but the need for implicit
> parameters has not gone away
>  - Scoped locals, because of their effective finality and dynamic
> scoping, offer a programming model that is a better fit for virtual
> threads, but, even in the absence of virtual threads, are an enabler for
> structured concurrency
>  - The programming model constraints enable a better-performing
> implementation
> 
> In reading the draft, these separate motivations seem somewhat tangled.
> All the words make sense, but a reader has a hard time coming away with
> a clear picture of "so, why did we do this exactly, besides that its
> cool and faster?"
> 
> A possible way to untangle this is:
> 
>  - the underlying use cases: various forms of implicit context
> (transaction context, implicit parameters, leaving breadcrumbs for your
> future self.)
>  - the existing solution: thread locals.  ThreadLocals are effectively
> mutable per-thread globals.  The unconstrained mutability makes them
> hard to optimize.  ThreadLocals interact poorly with pooled threads.
>  - Here comes Loom!  We no longer need to pool threads.  So, why are
> TLs not good enough?
>  - The more constrained programming model of SLs enables two big new
> benefits:
>    - structured concurrency, which is applicable to virtual and
> non-virtual threads alike
>    - better optimization of inheritance and get operations
> 
> 
> 
> 
> 
> 
> 
> On 5/12/2021 10:42 AM, Andrew Haley 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


Re: JEP draft: Scope Locals

2021-05-15 Thread Andrew Haley
On 5/15/21 10:10 AM, Alex Otenko wrote:
> ScopeLocal.where(...).run(...).finally(...) would be nice.

Is that different from try ... run ... finally ?

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



Re: JEP draft: Scope Locals

2021-05-15 Thread Andrew Haley
On 5/14/21 8: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.

This requirement sounds similar, but I think it's really a different idea.
Scope locals are only binding data to names and don't attempt to handle
lifetime issues. Apart from anything else, that would require a more
heavyweight mechanism than scope locals, which need to be very lightweight
for some applications.

Over at Loom we've been talking about lifetimes and Ron has some ideas, but
scope locals are not likely to be that thing.

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



Re: JEP draft: Scope Locals

2021-05-15 Thread Andrew Haley
On 5/15/21 12:28 AM, Nathan Reynolds wrote:
> What about cleanup of the scoped objects?  For example, how do I ensure
> close() or some cleanup method is called at the right time on the scoped
> object?  Do I need to use a Weak/Soft/PhantomReference and a ReferenceQueue
> to track the object?  Do I use Cleaner?  Does the object need to implement
> finalize()... hopefully not?

All a scope local does is bind a value to a name, in a scope. This isn't
about C++-style destructors, which have their merits but are a different
subject.

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



Re: JEP draft: Scope Locals

2021-05-14 Thread Brian Goetz
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 {
ScopedVariable begin();
}

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

Optional> createForChildScope();
Optional> createSnapshot();
}

static final ScopeLocal 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  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://keybase.io/andrewhaley
EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671







Re: JEP draft: Scope Locals

2021-05-14 Thread Andrew Haley
On 5/14/21 3:45 PM, Brian Goetz wrote:
> Where I think the JEP draft (the exposition, not the feature design) 
> could be improved is to make the relationship to TL more clear.  IMO, 
> this is a "more modern" TL design; it addresses the same primary use 
> cases, without the unconstrained mutability of TLs.  The safety that 
> comes from the immutability enables some new concurrent patterns (e.g., 
> structured concurrency) and some nice optimizations.

Thank you, that's a nice summary. I was trying to describe scope locals
in a stand-alone way, but everything that a scope local can do can also
be done by a TL, albeit with some clumsiness. I should be more explicit
about that.

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



Re: JEP draft: Scope Locals

2021-05-14 Thread Brian Goetz
Its simpler than you're making it.  Think of the motivating use cases 
for ThreadLocal, such as when a container calls into user code, and the 
user code calls back into the container, and we need to keep track of 
{transaction, security, etc} context, and it is impractical to pass them 
all through as parameters, or enumerate the possible callbacks.  The 
motivation here is the same; while the model is constrained, which 
eliminates some of the ways TLs are used today, these are "better TLs 
for better threads".  (If you believe that the motivation here is 
"poorly written libraries", then you believe that all libraries and 
frameworks that use TL are also poorly written.)


Where I think the JEP draft (the exposition, not the feature design) 
could be improved is to make the relationship to TL more clear.  IMO, 
this is a "more modern" TL design; it addresses the same primary use 
cases, without the unconstrained mutability of TLs.  The safety that 
comes from the immutability enables some new concurrent patterns (e.g., 
structured concurrency) and some nice optimizations.


On 5/14/2021 8:51 AM, Alan Snyder wrote:

The essence of the example seems to be that the callback is supporting multiple 
client contexts and when called needs to act relative to the specific client 
context.

The callback does not get a parameter identifying the client context because 
the library that calls it does not know anything about these client contexts.

Effectively, the callback uses the calling thread as a way to identify the 
client context.

That is not new. The ability to shadow/snapshot appears to be new, but it is 
still based on the thread as the ultimate context identifier.

Did I get it right?

If so, perhaps it would be less confusing if “Scope Locals” instead were called 
“Scoped Thread Locals”.






On May 14, 2021, at 1:28 AM, Andrew Haley  wrote:

On 5/13/21 4:59 PM, Alan Snyder wrote:



On May 13, 2021, at 2:03 AM, Andrew Haley  wrote:

On 5/12/21 8:12 PM, Alan Snyder wrote:

 From the motivation section:


So you want to invoke a method |X| in a library that later calls back into your 
code. In your callback you need some context, perhaps a transaction ID or some 
|File| instances. However, |X| provides no way to pass a reference through 
their code into your callback. What are you to do?

When I read this, my first thought was… pass a callback that is bound to the 
context.

I suppose the example you have in mind has a fixed callback.

Fixed? It's a callback, of some form.

What I meant was that the callback has a larger scope than the method call. 
Otherwise, you could bind the context to the callback object.

I don't know quite what you mean by "larger scope," but clearly, if you
can explicitly pass a callback created by a lambda, that's a more explicit
solution.


Is this a common practice? Doesn’t it point to a deficiency in the library API?

Not necessarily. For example, say you want to give access to a particular
resource (a log stream, say) to trusted callees. Nobody AFAIK passes a log
stream as an explicit argument through business logic because that's just
too much clutter.

That sounds interesting, but I’m not following how scope locals would be used 
to solve this problem.

In the outer scope you bind a logger scope local, and all transitive callees
can use that.

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





Re: JEP draft: Scope Locals

2021-05-14 Thread Alan Snyder
The essence of the example seems to be that the callback is supporting multiple 
client contexts and when called needs to act relative to the specific client 
context.

The callback does not get a parameter identifying the client context because 
the library that calls it does not know anything about these client contexts.

Effectively, the callback uses the calling thread as a way to identify the 
client context.

That is not new. The ability to shadow/snapshot appears to be new, but it is 
still based on the thread as the ultimate context identifier.

Did I get it right?

If so, perhaps it would be less confusing if “Scope Locals” instead were called 
“Scoped Thread Locals”.





> On May 14, 2021, at 1:28 AM, Andrew Haley  wrote:
> 
> On 5/13/21 4:59 PM, Alan Snyder wrote:
>> 
>> 
>>> On May 13, 2021, at 2:03 AM, Andrew Haley  wrote:
>>> 
>>> On 5/12/21 8:12 PM, Alan Snyder wrote:
 From the motivation section:
 
> So you want to invoke a method |X| in a library that later calls back 
> into your code. In your callback you need some context, perhaps a 
> transaction ID or some |File| instances. However, |X| provides no way to 
> pass a reference through their code into your callback. What are you to 
> do?
 
 When I read this, my first thought was… pass a callback that is bound to 
 the context.
 
 I suppose the example you have in mind has a fixed callback.
>>> 
>>> Fixed? It's a callback, of some form.
>> 
>> What I meant was that the callback has a larger scope than the method call. 
>> Otherwise, you could bind the context to the callback object.
> 
> I don't know quite what you mean by "larger scope," but clearly, if you
> can explicitly pass a callback created by a lambda, that's a more explicit
> solution.
> 
 Is this a common practice? Doesn’t it point to a deficiency in the library 
 API?
>>> 
>>> Not necessarily. For example, say you want to give access to a particular
>>> resource (a log stream, say) to trusted callees. Nobody AFAIK passes a log
>>> stream as an explicit argument through business logic because that's just
>>> too much clutter.
>> 
>> That sounds interesting, but I’m not following how scope locals would be 
>> used to solve this problem.
> 
> In the outer scope you bind a logger scope local, and all transitive callees
> can use that.
> 
> -- 
> Andrew Haley  (he/him)
> Java Platform Lead Engineer
> Red Hat UK Ltd. 
> https://keybase.io/andrewhaley
> EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
> 



Re: JEP draft: Scope Locals

2021-05-14 Thread Andrew Haley
On 5/13/21 4:59 PM, Alan Snyder wrote:
> 
> 
>> On May 13, 2021, at 2:03 AM, Andrew Haley  wrote:
>>
>> On 5/12/21 8:12 PM, Alan Snyder wrote:
>>> From the motivation section:
>>>
 So you want to invoke a method |X| in a library that later calls back into 
 your code. In your callback you need some context, perhaps a transaction 
 ID or some |File| instances. However, |X| provides no way to pass a 
 reference through their code into your callback. What are you to do?
>>>
>>> When I read this, my first thought was… pass a callback that is bound to 
>>> the context.
>>>
>>> I suppose the example you have in mind has a fixed callback.
>>
>> Fixed? It's a callback, of some form.
> 
> What I meant was that the callback has a larger scope than the method call. 
> Otherwise, you could bind the context to the callback object.

I don't know quite what you mean by "larger scope," but clearly, if you
can explicitly pass a callback created by a lambda, that's a more explicit
solution.

>>> Is this a common practice? Doesn’t it point to a deficiency in the library 
>>> API?
>>
>> Not necessarily. For example, say you want to give access to a particular
>> resource (a log stream, say) to trusted callees. Nobody AFAIK passes a log
>> stream as an explicit argument through business logic because that's just
>> too much clutter.
> 
> That sounds interesting, but I’m not following how scope locals would be used 
> to solve this problem.

In the outer scope you bind a logger scope local, and all transitive callees
can use that.

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



Re: JEP draft: Scope Locals

2021-05-13 Thread Pedro Lamarão
Em qui., 13 de mai. de 2021 às 13:36, Alan Snyder 
escreveu:


> >> Is this a common practice? Doesn’t it point to a deficiency in the
> library API?
> >
> > Not necessarily. For example, say you want to give access to a particular
> > resource (a log stream, say) to trusted callees. Nobody AFAIK passes a
> log
> > stream as an explicit argument through business logic because that's just
> > too much clutter.
> >
>
> That sounds interesting, but I’m not following how scope locals would be
> used to solve this problem.
>

It appears to me that, in this context, scoped locals is inserting itself
inside a hierarchy of "overrides".
We already have mechanisms which provide "context" capable of carrying
"overrides", such as process environment variables and JVM system
properties.
Those are too global for certain uses such as "overriding" a single method
call.
So, in a great hierarchy of "overrides", we would have, from the largest to
the smallest: process environment variables > JVM system properties > *scoped
locals* > method call parameters.
Things like these are recurrent in the design of "I/O cancelability", where
people are always discovering the need for things like "timeout scopes".
It could be argued that "cancelable" APIs must have some "cancellation
token " parameter.
But there are cases where some domain interface, which was never designed
with this in mind, must be implemented over network protocols.
This is the case when you must implement a KeyStore or a Cipher with
remotely managed private keys.
Since you cannot alter KeyStore to pass a "cancellation token" to single
methods, you must insert the token as a sort of "override" thing in some
parallel storage.

-- 
Pedro Lamarão
https://www.prodist.com.br
Securing Critical Systems
Tel: +55 11 4380-6585

Antes de imprimir esta mensagem e seus anexos, certifique-se que seja
realmente necessário.
Proteger o meio ambiente é nosso dever.
Before printing this e-mail or attachments, be sure it is necessary.
It is in our hands to protect the environment.


Re: JEP draft: Scope Locals

2021-05-13 Thread Alan Snyder



> On May 13, 2021, at 2:03 AM, Andrew Haley  wrote:
> 
> On 5/12/21 8:12 PM, Alan Snyder wrote:
>> From the motivation section:
>> 
>>> So you want to invoke a method |X| in a library that later calls back into 
>>> your code. In your callback you need some context, perhaps a transaction ID 
>>> or some |File| instances. However, |X| provides no way to pass a reference 
>>> through their code into your callback. What are you to do?
>> 
>> When I read this, my first thought was… pass a callback that is bound to the 
>> context.
>> 
>> I suppose the example you have in mind has a fixed callback.
> 
> Fixed? It's a callback, of some form.
> 

What I meant was that the callback has a larger scope than the method call. 
Otherwise, you could bind the context to the callback object.


>> Is this a common practice? Doesn’t it point to a deficiency in the library 
>> API?
> 
> Not necessarily. For example, say you want to give access to a particular
> resource (a log stream, say) to trusted callees. Nobody AFAIK passes a log
> stream as an explicit argument through business logic because that's just
> too much clutter.
> 

That sounds interesting, but I’m not following how scope locals would be used 
to solve this problem.


>> Is this feature just a workaround for poorly designed libraries, or are 
>> there other examples?
> 
> It's not really about poor design as much as separation of concerns.
> Intermediate libraries have no way to know what might need to be passed
> to callees, and in many cases it's better isolation if they don't get to
> find out. The latter is especially true of passing security permissions.
> 
> Also, there is evolution of libraries: with scope locals you don't need
> to change library interfaces to add useful capabilities like logging.
> 
> -- 
> Andrew Haley  (he/him)
> Java Platform Lead Engineer
> Red Hat UK Ltd. 
> https://keybase.io/andrewhaley
> EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
> 



Re: JEP draft: Scope Locals

2021-05-13 Thread Andrew Haley
On 5/12/21 8:12 PM, Alan Snyder wrote:
> From the motivation section:
> 
>> So you want to invoke a method |X| in a library that later calls back into 
>> your code. In your callback you need some context, perhaps a transaction ID 
>> or some |File| instances. However, |X| provides no way to pass a reference 
>> through their code into your callback. What are you to do?
> 
> When I read this, my first thought was… pass a callback that is bound to the 
> context.
> 
> I suppose the example you have in mind has a fixed callback.

Fixed? It's a callback, of some form.

> Is this a common practice? Doesn’t it point to a deficiency in the library 
> API?

Not necessarily. For example, say you want to give access to a particular
resource (a log stream, say) to trusted callees. Nobody AFAIK passes a log
stream as an explicit argument through business logic because that's just
too much clutter.

> Is this feature just a workaround for poorly designed libraries, or are there 
> other examples?

It's not really about poor design as much as separation of concerns.
Intermediate libraries have no way to know what might need to be passed
to callees, and in many cases it's better isolation if they don't get to
find out. The latter is especially true of passing security permissions.

Also, there is evolution of libraries: with scope locals you don't need
to change library interfaces to add useful capabilities like logging.

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



Re: JEP draft: Scope Locals

2021-05-12 Thread Alan Snyder
From the motivation section:

> So you want to invoke a method X in a library that later calls back into your 
> code. In your callback you need some context, perhaps a transaction ID or 
> some File instances. However, X provides no way to pass a reference through 
> their code into your callback. What are you to do?

When I read this, my first thought was… pass a callback that is bound to the 
context.

I suppose the example you have in mind has a fixed callback. Is this a common 
practice? Doesn’t it point to a deficiency in the library API?

Is this feature just a workaround for poorly designed libraries, or are there 
other examples?

  Alan



Re: JEP draft: Scope Locals

2021-05-12 Thread Brian Goetz

Scope locals have come together nicely.

I have some vague thoughts on the presentation of the JEP draft.  There 
are (at least) three intertwined things in the motivation:


 - ThreadLocal (and ITL especially) were always compromises, and with 
the advent of Loom, have become untenable -- but the need for implicit 
parameters has not gone away
 - Scoped locals, because of their effective finality and dynamic 
scoping, offer a programming model that is a better fit for virtual 
threads, but, even in the absence of virtual threads, are an enabler for 
structured concurrency
 - The programming model constraints enable a better-performing 
implementation


In reading the draft, these separate motivations seem somewhat tangled.  
All the words make sense, but a reader has a hard time coming away with 
a clear picture of "so, why did we do this exactly, besides that its 
cool and faster?"


A possible way to untangle this is:

 - the underlying use cases: various forms of implicit context 
(transaction context, implicit parameters, leaving breadcrumbs for your 
future self.)
 - the existing solution: thread locals.  ThreadLocals are effectively 
mutable per-thread globals.  The unconstrained mutability makes them 
hard to optimize.  ThreadLocals interact poorly with pooled threads.
 - Here comes Loom!  We no longer need to pool threads.  So, why are 
TLs not good enough?
 - The more constrained programming model of SLs enables two big new 
benefits:
   - structured concurrency, which is applicable to virtual and 
non-virtual threads alike

   - better optimization of inheritance and get operations







On 5/12/2021 10:42 AM, Andrew Haley 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