ScopedValue polyfill follow-up [try-with-resources]

Davor Hrg hrgdavor at gmail.com
Mon Mar 31 22:19:07 UTC 2025


Hi,

makeCurrent is not my naming :) ... it is from OpenTelemetry.

my interest in try catch version is because I saw it in OT, but I can live
without that variant.

I am trying to integrate ScopedValue  patterns into my projects, and am a
bit worried it will be in preview in Java 25,
So I am trying to make something for myself to be easily replaceable by
ScopedValues after it is official (not preview).










On Mon, Mar 31, 2025 at 11:45 PM Ron Pressler <ron.pressler at oracle.com>
wrote:

> Hi.
>
> If method call `f()` returns an `AutoCloseable` whose `close` method
> doesn’t block, allocate, or perform any other operation that may fail, it
> may seem as if the following pattern — of “just doing it right” — should
> always work:
>
>     try (var x = f()) { … }
>
> Unfortunately, that is not the case because the very call to `close` may
> immediately fail with a StackOverflowError. In some cases of try/finally,
> the JDK makes special accommodations for such a situation, while in others
> it’s deemed acceptable, as a VirtualMachineError may leave the thread in
> some inconsistent state anyway. However, the case of ScopedValue is
> especially sensitive and needs to behave appropriately even in the face of
> a VME. This is a general problem that we may address in the future.
>
> P.S.
>
> As you may imagine, it is rare for us to have not considered various
> ideas, especially when they seem reasonable. If we don’t do something that
> seems reasonable, either there’s a problem with it, or we’ve decided to
> postpone it for later (possibly because it may interact with some future
> planned feature). It is better to frame feedback in the form of “I’ve tried
> to feature and run into the following problem…” At the very least it helps
> us see what problems people actually run into as opposed to what problems
> people speculate that they or others may run into.
>
>
> — Ron
>
> > On 31 Mar 2025, at 18:59, Chapman Flack <chap at anastigmatix.net> wrote:
> >
> > Hi,
> >
> > On 02/09/25 06:58, hrgdavor at gmail.com (Davor Hrg) wrote:
> >> OT Context can be used as autocloseable in try with resources.
> >> ScopedValue is from what I can see meant to be wrapped in Runnable or
> >> Callable.
> >>
> >> My concern may be misguided, but I do want to ask if there is any
> overhead
> >> to worry about with creating a lambda to create a scope,
> >>
> >> runWhere(CTX, value,()->{
> >>    //do something
> >> });
> >>
> >> versus try with resources
> >>
> >> try (Scope ignored = Context.current().with(CTX.KEY,
> value).makeCurrent()) {
> >>    //do something
> >> }
> >
> >
> > I would like to add my voice in favor of also offering a
> try-with-resources
> > friendly method. My concern is not with overhead, but simply with
> enabling
> > more flexibility in API design around the new feature.
> >
> > I am very pleased with the addition of the CallableOp functional
> interface
> > in the third preview. Unless I messed up my quick search in jshell just
> now,
> > even in Java 24, CallableOp still seems to be the very first, solitary,
> > only, public, unqualified-exported, functional interface with a generic
> > thrown exception type in the whole transitive closure of java.se.
> >
> > That means even if there were nothing else good about scoped values,
> > just having CallableOp land as a standard functional interface in Java
> > immediately makes cleaner designs possible when designing APIs for other
> > things. (Maybe it could even be introduced in a less obscure place,
> > right in java.util.function, perhaps?)
> >
> > While it's easy enough to roll my own such interface and I've done so
> > for countless internal uses, I've always been reluctant to expose it in
> > an API that I design, because then I'd just be contributing to a
> > proliferation of APIs expecting differently-named equivalent functional
> > interfaces while waiting for Java to provide a standard one, and
> consigning
> > anyone who needs make two or more such APIs interoperate to the drudgery
> > of converting one lambda to another via method reference.
> >
> > I note in passing that there is still an opportunity for Java to provide
> > a subinterface of AutoCloseable with a generic thrown exception type.
> > My search in Java 24 this morning turned up exactly zero of those. Again,
> > I've rolled those too for my own use but never wanted to clutter an API
> > with a one-off version.
> >
> > When scoped values land, CallableOp will not be only convenient for code
> > directly manipulating scoped values. It will also allow API design where
> > library A can pass a CallableOp to library B to be executed in a certain
> > mode of B's operation, which B may well implement using a scoped value
> > under the hood but that's none of A's business.
> >
> > The trouble is, in polyfilling such a design before non-preview scoped
> > values land, there isn't yet any available standard functional interface
> > that can stand in for CallableOp. A polyfill would have to roll its own
> > and there would be disruption in switching later.
> >
> > And that is why I would like to suggest offering the choice of two modes
> > of operation for scoped values:
> >
> > ScopedValue.where(V, foo).call(() -> {
> >  ... do ...
> >  ... some ...
> >  ... work ...
> >  return thing;
> > });
> >
> > or
> >
> > try (var _ = ScopedValue.where(V, foo).makeCurrent() ) {
> >  ... do ...
> >  ... some ...
> >  ... work ...
> >  result = thing;
> > }
> >
> > The two are roughly equivalent in convenience and expressiveness, and
> > could easily both be offered. (I've used Davor Hrg's name makeCurrent
> > above, but others might work ... inScope() ?)
> >
> > But while it is trivial to implement an API that works the first way
> > on top of one that works the second way, it's impossible to do the
> reverse.
> >
> > If there is now some library B whose API for "be in such-and-such mode
> > for this bit of work I have to do" already has the first pattern, and
> > that library now wants to migrate to using ScopedValue under the hood,
> > it easily can. Even if B's API exposes some differently-named functional
> > interface to the caller, it can easily cast a method reference to
> > CallableOp under the hood and no client code disruption results.
> >
> > On the other hand, if there is a library whose API for "be in
> such-and-such
> > mode for this bit of work" already has the second pattern, and now that
> > library wants to migrate to using ScopedValue under the hood ... IT
> CAN'T.
> >
> >
> > And in the present state of affairs, I would suspect that more existing
> > libraries use that second pattern than the first, because of the long-
> > standing lack of a standard functional interface like CallableOp.
> >
> >
> > Some discussion I've seen online elsewhere suggested there might have
> > been a concern about supporting a try-with-resources pattern because
> > nothing actually forces the programmer to call the method in a try-
> > with-resources, and that could make it 'unsafe'.
> >
> > But it seems to me that 'safety' divides into at least three categories:
> >
> > 1. Safe - this construct can't be used in any way that would let
> >   Bad Things happen. The call(CallableOp) pattern fits here.
> >
> > 2. Unsafe - this construct's very existence could allow Bad Things
> >   to get you even if you use it right. Nothing proposed fits here.
> >
> > 3. So just do it right. Yeah, Bad Things could happen if you don't.
> >   But you're the one writing the code, you know what a try-with-resources
> >   looks like, and you're happy when your code works, so you'll do it
> right.
> >
> > Case 3 to me seems benign enough that I wouldn't use it as a reason
> > to make one whole pattern of API design unimplementable over scoped
> values.
> >
> > I was not able to find a lot of earlier discussion in the list archives
> > (I've only looked back a year). I did see a point made that a ScopedValue
> > .get() can now be assured to produce the same value anywhere in a method,
> > where a try-with-resources option would allow it to have a different
> > value within a try block. That may perhaps complicate optimization, but
> > maybe not insurmountably? In the archived discussion I saw, there was
> > some talk of allowing arbitrary in()/out() of the scope, but I do not
> > propose that. All I suggest is returning an AutoCloseable subtype with
> > one close() method that closes it once and for all.
> >
> > There may also be a simplifying consideration. In the example I outlined
> > above where a library B uses scoped values under the hood, and may wrap
> > a ScopedValue AutoCloseable in some AutoCloseable of its own that it
> > returns to its caller A for use in a try-with-resources, then yes,
> > different bytecode ranges in that A method may correspond to different
> > scoped values for B. But chances are A has no access to B's scoped values
> > anyway. And in any method of B called within that try block in A,
> > B's scoped values will be unchanging.
> >
> > Thanks for considering my suggestion, and I hope it has contributed
> > something constructive to the discussion.
> >
> > Regards,
> > Chapman Flack
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20250401/d7deabf9/attachment-0001.htm>


More information about the loom-dev mailing list