-
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
Support leak-free closeable resources transfer via channel #1936
Comments
Since is was a real pain to reach proper code for #1092, will this come with complete example of a workerpool with closable resources? :) |
Should |
Unfortunately, that is impractical to implement, since some places that encounter the situation with "the resource that is being lost" lack any context to perform a suspending operation (e.g. In those cases were "closing a resource" is a long-running and time-consuming operation (requiring UI interaction, DB write, network, etc) the recommended pattern is:
|
Understood! Then the |
When exception is thrown in |
One gotcha is that OutputStream.close() is blocking and could take arbitrarily long. The java.io blocking APIs don't have a mechanism to cancel, only to close cleanly. Presumably the mitigation is to send that work to the IO dispatcher. (Maybe that practice should be recommended?) |
@elizarov Is it expected, that for elements of Unlimited Channel, on cancel, import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
suspend fun main(){
val channelBuffered = Channel<Int>(10) { println("RELEASE[BUFFER]: $it") }
val channelUnlimited = Channel<Int>(Channel.UNLIMITED) { println("RELEASE[UNLIMITED]: $it") }
repeat(5) {
channelBuffered.offer(it)
channelUnlimited.offer(it)
}
channelBuffered.cancel()
channelUnlimited.cancel()
} prints:
I would expect, that |
…otlin#1937) This is a problematic for Android when Main dispatcher is cancelled on destroyed activity. Atomic nature of channels is designed to prevent loss of elements, which is really not an issue for a typical application, but creates problem when used with channels. * Internal suspendAtomicCancellableCoroutine -> suspendCancellableCoroutine * Internal suspendAtomicCancellableCoroutineReusable -> suspendCancellableCoroutineReusable * Remove atomic cancellation from docs * Ensures that flowOn does not resume downstream after cancellation. * MODE_ATOMIC_DEFAULT renamed into MODE_ATOMIC * Introduced MODE_CANCELLABLE_REUSABLE to track suspendCancellableCoroutineReusable * Better documentation for MODE_XXX constants. * Added stress test for proper handling of MODE_CANCELLABLE_REUSABLE and fixed test for Kotlin#1123 bug with job.join (working in MODE_CANCELLABLE) that was not properly failing in the absence of the proper code in CancellableContinuationImpl.getResult * Added test for Flow.combine that should be fixed * Support extended invokeOnCancellation contract * Introduced internal tryResumeAtomic * Channel onUnderliveredElement is introduced as a replacement. Fixes Kotlin#1265 Fixes Kotlin#1813 Fixes Kotlin#1915 Fixes Kotlin#1936 Co-authored-by: Louis CAD <[email protected]> Co-authored-by: Vsevolod Tolstopyatov <[email protected]>
…otlin#1937) This is a problematic for Android when Main dispatcher is cancelled on destroyed activity. Atomic nature of channels is designed to prevent loss of elements, which is really not an issue for a typical application, but creates problem when used with channels. * Internal suspendAtomicCancellableCoroutine -> suspendCancellableCoroutine * Internal suspendAtomicCancellableCoroutineReusable -> suspendCancellableCoroutineReusable * Remove atomic cancellation from docs * Ensures that flowOn does not resume downstream after cancellation. * MODE_ATOMIC_DEFAULT renamed into MODE_ATOMIC * Introduced MODE_CANCELLABLE_REUSABLE to track suspendCancellableCoroutineReusable * Better documentation for MODE_XXX constants. * Added stress test for proper handling of MODE_CANCELLABLE_REUSABLE and fixed test for Kotlin#1123 bug with job.join (working in MODE_CANCELLABLE) that was not properly failing in the absence of the proper code in CancellableContinuationImpl.getResult * Added test for Flow.combine that should be fixed * Support extended invokeOnCancellation contract * Introduced internal tryResumeAtomic * Channel onUnderliveredElement is introduced as a replacement. Fixes Kotlin#1265 Fixes Kotlin#1813 Fixes Kotlin#1915 Fixes Kotlin#1936 Co-authored-by: Louis CAD <[email protected]> Co-authored-by: Vsevolod Tolstopyatov <[email protected]>
A |
Any way use same feature to the flow? |
…tate and SideEffects (strictly not recommended!) * Previous state will be properly closed on change * Side effects closed when not delivered For leak-free transfer details see "Undelivered elements" section in [Channel](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/) documentation. Also see: Kotlin/kotlinx.coroutines#1936 Signed-off-by: Artyom Shendrik <[email protected]>
…tate and SideEffects (strictly not recommended!) * Previous state will be properly closed on change * Side effects closed when not delivered For leak-free transfer details see "Undelivered elements" section in [Channel](https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-channel/) documentation. Also see: Kotlin/kotlinx.coroutines#1936 Signed-off-by: Artyom Shendrik <[email protected]>
The change in #1813 removes support for "atomic cancellation", making it impossible to transfer a resource via a channel from one coroutine to another. When a closeable resource (like open file or a handle to another native resource) is transferred via channel from one coroutine to another it can be lost if either send or receive operation are cancelled in transit.
We introduce the following replacement. A
Channel()
constructor function gets and additional optional parameter:onUndeliveredElement: ((E) -> Unit)? = null
When
onUndeliveredElement
option parameter is set, the corresponding function is called once for each element that was sent to the channel and is being lost due to cancellation, which can happen in the following cases:send
operation was cancelled before it had a chance to actually send the element.receive
operation retrieved the element from the channel but was cancelled when trying to return it the caller.onUndeliveredElement
is called on every remaining element in the channel's buffer.Note, that
onUndeliveredElement
is called synchronously in an arbitrary context. It should be fast, non-blocking, and should not throw exceptions. Any exception thrown byonUndeliveredElement
is wrapped into an internal runtime exception which is either rethrown or handed off to the exception handler in the current context (CoroutineExceptionHandler
) when one is available.UPDATE: The final name of the parameter is
onUndeliveredElement
.The text was updated successfully, but these errors were encountered: