-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Work with ThreadLocal-sensitive Components #119
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
Comments
Coroutine's analog to To interoperate with ThradLocal-using libraries you need to implement a custom
To use it with coroutines, you'll need to implement a context that keeps the current value of
To use it in your coroutines, you wrap the dispatcher that you want to use with
The implementation above would also track any changes to the thread-local that was done and store it in this context, so this way multiple invocation can share "thread-local" data via context. I do think we need a ready-to-use implementation for SLF4j in the |
I've reposted this Q&A to StackOverflow: https://stackoverflow.com/questions/46227462/how-to-use-code-that-relies-on-threadlocal-with-kotlin-coroutines/46227463 |
We should really have it out-of-the box in a separate integration module. |
@elizarov can you take a look at link below? It is related to Spring Security Context (thread local), but there is an interceptor that cache the coroutine context so I cannot use the approach you mention. Due that the continuation is created in the thread in witch Spring Security Context "exists", I can capture it in the continuation and then apply your wrap solution. konrad-kaminski/spring-kotlin-coroutine#19 thanks in advance and sorry to link to another repository. |
@elizarov - I will try to contribute it. |
The problem with "wrapping dispatcher" approach that I've provided in SO Q&A is that this "wrapped dispatcher" would get lost if dispatcher gets replaced. Consider, the proposed SLF4J MDC context from #403, for example. If you do
But then in
I'm implementing an alternative approach, where you can implement and register a special extension point called
Now, |
…cal sensitive code * Debug thread name is redesigned in the style of "thread local" where the name of thread reflect the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…cal sensitive code * Debug thread name is redesigned in the style of "thread local" where the name of thread reflect the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
The downside of the API that is currently proposed in PR #454 is that its overhead for each coroutine suspend/resume is |
…cal sensitive code * Debug thread name is redesigned in the style of "thread local" where the name of thread reflect the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…cal sensitive code * Debug thread name is redesigned in the style of "thread local" where the name of thread reflect the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
API in PR #454 now looks like this. In your to use it you have to define your own context element that implements
The implementation is quite efficient now. Its cost is |
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
…sitive code * Debug thread name is redesigned using ThreadContextElement API where the name of thread reflects the name of currently coroutine. * Intrinsics for startCoroutineUndispatched that correspond to CoroutineStart.UNDISPATCHED properly update coroutine context. * New intrinsics named startCoroutineUnintercepted are introduced. They do not update thread context. * withContext logic is fixed properly update context is various situations. * DebugThreadNameTest is introduced. * Reporting of unhandled errors in TestBase is improved. Its CoroutineExceptionHandler records but does not rethrow exception. This makes sure that failed tests actually fail and do not hang in recursive attempt to handle unhandled coroutine exception. Fixes #119
For my projects, I used to do write a simple Let's for the sake of an example say: We want to have block-style db-connections. But only one connection per coroutine. So when we nest the db-connection-blocks we will keep the transaction state and other db state. object MyDatabaseAccess {
val connection = CoroutineLocal<Connection>() // <-- matter of interest
// block-style db-connection
suspend fun <T> connected( block: suspend (Connection) -> T ): T {
var con : Connection? = connection.get() // <-- matter of interest
val newConnection = (con==null || con.isClosed)
if (newConnection) {
con = TODO( "create connection" )
connection.set(con) // <-- matter of interest
}
val ret = block(con!!)
if (newConnection) {
con.close()
connection.set(null) // <-- matter of interest
}
return ret
}
} And the class CoroutineLocal<T> {
val values = WeakHashMap<CoroutineContext,T>()
suspend fun get(): T? =
values[coroutineContext]
suspend fun set(value: T?) {
if (value==null)
values.remove(coroutineContext)
else
values[coroutineContext] = value
}
} |
Using LazyTraceAsyncTaskExecutor(...).as CoroutineDispatcher as follows:
Output:
|
Many application trace framework use
ThreadLocal
to store the call context of a application, like the SLF4j MDC, CAT , and other distributed trace system(which use thread local for trace the context in some async situation).But the coroutine will be dispatched to a unspecified thread to execute, how can i make the tracing work?
The text was updated successfully, but these errors were encountered: