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
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
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" <brian.goetz@oracle.com> À: "Andrew Haley" <aph@redhat.com>, "core-libs-dev" <core-libs-dev@openjdk.java.net>, "loom-dev" <loom-dev@openjdk.java.net> 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
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
On May 13, 2021, at 2:03 AM, Andrew Haley <aph@redhat.com> 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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
Em qui., 13 de mai. de 2021 às 13:36, Alan Snyder <javalists@cbfiddle.com> 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.
On 5/13/21 4:59 PM, Alan Snyder wrote:
On May 13, 2021, at 2:03 AM, Andrew Haley <aph@redhat.com> 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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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 <aph@redhat.com> wrote:
On 5/13/21 4:59 PM, Alan Snyder wrote:
On May 13, 2021, at 2:03 AM, Andrew Haley <aph@redhat.com> 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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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 <aph@redhat.com> wrote:
On 5/13/21 4:59 PM, Alan Snyder wrote:
On May 13, 2021, at 2:03 AM, Andrew Haley <aph@redhat.com> 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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
On Wed, May 19, 2021 at 4:01 AM Andrew Haley <aph@redhat.com> 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
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
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
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
On Wed, May 12, 2021 at 9:43 AM Andrew Haley <aph@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.
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<T>` plus a `T` to pass in, a `Function<T, R>` 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/bas... [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/wild... -- - DML • he/him
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://www.redhat.com> https://keybase.io/andrewhaley EAC8 43EB D3EF DB98 CC77 2FAD A5CD 6035 332F A671
participants (7)
-
Alan Snyder
-
Andrew Haley
-
Brian Goetz
-
David Lloyd
-
Pedro Lamarão
-
Peter Levart
-
Remi Forax