<div dir="ltr">Hi, and thanks for your work on StructuredTaskScope. I recently adapted it for use in our services to handle (a) propagation of the OpenTelemetry Context, and (b) the richer cancellation model we use. Overall it went fairly well, and I'll give a few details and some minor feedback in case that's of interest.<div><br></div><div>Background: We have an implementation of io.opentelemetry.context.Context for the usual purposes of passing around tracing, logging, authentication, and other context. We also added a cancellation signal in that Context implementation, very much along the lines of grpc-java's Context/CancellableContext classes (which also makes it similar to golang's Context). Cancelling a parent Context cancels all the child contexts, and a Context can cancel itself when it reaches a Deadline, and callbacks can be registered to run upon cancellation. We do this because thread interruption alone is not a sufficient approach for cancellation when we need to cancel things like HTTP or gRPC calls, JDBC Statements, and other I/O which isn't interruptible. (I understand that it's not a goal of the current structured concurrency JEPs to improve cancellation beyond thread interruption).</div><div><br></div><div>Details:</div><div><br></div><div>StructuredTaskScope is a sealed interface with a hidden implementation, so this required that I make a wrapper class which I called CancellableTaskScope. I gave it exactly the same API though, so that it could be a drop-in replacement for StructuredTaskScope. It uses the same Joiners and Subtask types, and internally it delegates to a StructuredTaskScope.</div><div><br></div><div>In order to propagate the current Context to all forked tasks, I could not rely on the ScopedValue propagation of StructuredTaskScope, since ScopedValue's model is not compatible with io.opentelemetry.context.Context's approach of a makeCurrent() method which returns a Scope to close, meaning that Context still has to be stored in a ThreadLocal. So, I wrap the ThreadFactory for the task scope in a decorator which first makes the scope's Context current on all new threads.</div><div><br></div><div>This is where the main challenge arose: The StructuredTaskScope.Configuration interface has only withers, no accessors. So my CancellableTaskScope could not work with that same Configuration interface, as there was no way to get the configured ThreadFactory out of it so that it could be wrapped before delegating to StructuredTaskScope. I worked around this by duplicating the Configuration type in my class. That's not terrible, and I ended up adding an additional configuration parameter anyway for specifying an explicit parent Context.</div><div><br></div><div>Similarly, I take the timeout configured in the Configuration to set the Deadline on our Context, and I don't pass that timeout through to the StructuredTaskScope since our Context can manage it.</div><div><br></div><div>Propagating the Context to the forked tasks meant that our cancellation signal was also propagated to them. The fork and join methods in CancellableTaskScope are written to respect our Context cancellation. Next, I needed to cancel the scope's Context when the Joiner decided to cancel the StructuredTaskScope. This meant another decorator, this time wrapping the Joiner to spy on the boolean returned by onFork/onComplete.</div><div><br></div><div>There is one minor mismatch, in that our Context can be cancelled asynchronously at any time (such as a gRPC client cancelling a call), but the StructuredTaskScope can only be cancelled at specific times: by the owner thread calling close(), or by the Joiner when a task happens to be forked or completed. I don't think this is an issue in practice though, since the owner thread should respect an asynchronous cancellation signal and quickly proceed to calling close().</div><div><br></div><div>So StructuredTaskScope turned out to be reasonably well extended by composition even though a lot of it is sealed. I had some thoughts on the exceptions StructuredTaskScope throws, which I will leave to another post.</div><div><br></div><div>Regards,</div><div>Wesley Hill</div></div>