ScopedValue structured forking/forwarding API

Andrew Haley aph-open at littlepinkcloud.com
Mon Aug 4 11:03:01 UTC 2025


On 04/08/2025 11:01, Nikita Bobko wrote:
> Andrew Haley wrote:
>> I think we can make this robust if and only if ScopedValue call() blocks
>> until every forward() (or, equivalently, every bindings.join()) has run
>> to completion. This approach may also give us a way to support parallel
>> streams, etc., without compromising scoped values' temporal and
>> structural invariants. This is an attractive thought.
> 
> Yes, it's similar to how StructuredTaskScope.close works.

OK. It's a matter of ensuring this property.

> Andrew Haley wrote:
>> Unfortunately, even though failing to wait for children to terminate
>> would be a coding error, it couldn't cause an exception to be thrown
>> because that would leave threads running after the scope that launched
>> them had exited. We could try to interrupt the blocking threads, but if
>> they didn't respond we'd be stuck.
> 
> The similar problem exists in StructuredTaskScope API. AFAIK,
> StructuredTaskScope.close blocks until children are terminated and only
> after that it throws the exception.

Right. We'd be taking this internal JDK logic and making its access public.

> Andrew Haley wrote:
>> Also, doing this does sort-of imply that there has to be a handshake at
>> every scoped value binding
> 
> What do you mean?

There's a tension between making scoped value binding sites as 
lightweight as they can be, and cross-thread inheritance of bindings. 
ScopedValueContainers are used (and in effect managed) by 
ThreadContainers. ScopedValueContainers are not created unless we need 
them. The blocking logic (in popForcefully()) is not executed unless we 
actually use structured concurrency. This inter-thread handshaking is 
expensive, so I don't want popForcefully() to be executed unless 
inheritance is used.

> Andrew Haley wrote:
>> It also means that ScopedValue.Snapshot
>> would need to be mutable in order to ensure that a Snapshot instance
>> couldn't be reused once it was join()ed.
> 
> I think it's fine. StructuredTaskScope behaves in the similar manner.
> One can't join() it more than once.

Mmm, yes.

>> Pedro Lamarão wrote:
>>> Is this not the domain of the transformer or
>>> scheduler? shouldn't this be implemented in that layer?
>>
>> Andrew Haley wrote:
>>> In effect we already do exactly this with Java's
>>> jdk.internal.vm.Continuation class, but that's not available to client
>>> code such as Kotlin. So sure, it's the job of the transformer, but there
>>> isn't an API that such a transformer can use.
>>
>> May I inquire: what is a transformer? I briefly read some of the
>> jdk.internal.vm.Continuation code, but apparently not enough.
> 
> I am still curious about this.

It's not my usage, but I think it's a reference to the way in which 
suspend functions are transformed into continuation-passing style.

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


More information about the loom-dev mailing list