On 23/02/2026 14: 18, Michael Stover wrote: > Secondly, the propagation of ScopedValues in StructuredConcurrency is > nice, but it's limited to simply copying the reference to the > ScopedValue to all the sub-threads. For some purposes,
On 23/02/2026 14:18, Michael Stover wrote:
> Secondly, the propagation of ScopedValues in StructuredConcurrency is
> nice, but it's limited to simply copying the reference to the
> ScopedValue to all the sub-threads. For some purposes, that works fine,
> but there are surely cases where what we'd want is a child context
> created, probably from the parent.
By design, no copying takes place. The set of scoped value bindings is
made visible to the child. That set is immutable: nothing in it can be
altered, and nothing can be removed. When a child thread is started
there is no opportunity to inspect the inherited bindings to determine
any properties. All a child can do is create a superset of the parent's
bindings by adding bindings of its own.
> If the propagation routine could discover that a ScopedValue would
> prefer to create a child scoped value rather than just copying the
> parent scoped value to all sub-threads, then when creating a ScopedValue
> we could control how it propagates. If the ScopedValue implemented an
> interface such as "PropagatingScopedValue" or whatever, then it could
> have the opportunity to create it's child object for each sub-thread.
The ScopedValue API is deliberately simple, in order to have a small API
surface and to impose as little as possible runtime overhead on simple
usage patterns.
We didn't implement a non-inheritable scoped value because that's fairly
simple to do in user code.
public class MyKindOfScopedValue<T> {
private final ScopedValue<ValueHolder<T> > handle;
public MyKindOfScopedValue() {
handle = ScopedValue.newInstance();
}
public void runWith(T t, Runnable op) {
where(value, new ValueHolder(t)).run(op);
}
public T get() {
return handle.get().get();
}
... etc
}
class ValueHolder<T> {
private final Thread owner;
private final T value;
T get() {
if (owner == Thread.currentThread()) {
return value;
} else {
... something local here;
}
}
ValueHolder(T value) {
owner = Thread.currentThread();
this.value = value;
}
... etc
}