-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide facilities for non-threadsafe mutable coroutine context element #2839
Comments
The design that I would propose for this problem is to add a new method to the
UPDATED: It needs to have |
I think that'll work? Any overridable factory function called by the parent Would you consider making
I'll try this out. |
I added a new interface in the example implementation for two reasons:
|
As long as we don't have a use-case for this copying facility that is separate from |
The proposed signature, with or without the `CoroutineContext parameter, seems to work.
|
We're revisiting this issue.
Working counterexample: // Contains a “root” CopyableTraceThreadContextElement for someThreadLocal
// that manages happens-before writing back from the `ThreadLocal` to the
// coroutine's Element.
val dependencyInjectedContext
fun f() {
runBlocking(
dependencyInjectedContext
) {
api.setSomeThreadLocal(3)
launch {
check(api.getFromSomeThreadLocal() == 3)
api.setSomeThreadLocal(3)
withContext(
dependencyInjectedContext
) {
// This block has a problem. A second coroutine is using
// `dependencyInjectedContext`. Since `dependencyInjectedContext`
// is the `context` parameter to a coroutine builder, that
// TraceThreadContextElement _overrides_ the TraceThreadContextElement
// that copy-constructed for this child coroutine when
// `dependencyInjectedContext` was folded left onto the running context.
//
// This block should have `someThreadLocal.get() == 7` and its writes
// should be isolated. Instead, it's sharing an element with the outermost
// `runBlocking {}` coroutine, so its writes will be made visible to
// the grandparent coroutine in an unsound way, and resumptions may restore
// the grandparent's
api.setSomeThreadLocal(3)
}
}.join()
check(api.getFromSomeThreadLocal() == 3) // Passes only if the inner block doesn't write.
}
}
We're working on a replacement. |
…ine()`. (Kotlin#3025) * This is a `ThreadContextElement` that is copy-constructed when a new coroutine is created and inherits the context. Co-authored-by: Tyson Henning <yorick@google.com> Fixes Kotlin#2839
@yorickhenning please see the last section of the original design document, I've added a new implementation proposal. It seems like |
Following up, now that we've had the revised version in production for a while. Monitoring found ultra-rare consistency errors a few months ago - somewhere in the neighbourhood of P=1/10^12 to P=1/10^15. I assumed a data race remained. After getting to reliable heuristic reproduction in a unit test, the data race turned out to be #2930. Upgrading past the fix for that issue quieted monitoring, and we've found no new races so far. It works. |
…ine()`. (Kotlin#3025) * This is a `ThreadContextElement` that is copy-constructed when a new coroutine is created and inherits the context. Co-authored-by: Tyson Henning <yorick@google.com> Fixes Kotlin#2839
Consider the use-case of keeping a trace of operations. In thread-based programming, the data structure that keeps the current trace is stored in a
ThreadLocal
variable and gets updated by the corresponding tracing calls in the thread. With coroutines, one can useThreadContextElement
to keep a reference to this structure. However, this element will get automatically inherited by children of the coroutine which might execute concurrently. This concurrency poses a problem when the data structure keeping a trace is not thread-safe and making it thread-safe might be impractical for performance reasons. The proposed solution is to add some mechanism, that would let the coroutine context element to copy itself during the creation of concurrent coroutines so that there can be a guarantee that each copy is accessed only sequentially.The text was updated successfully, but these errors were encountered: