Skip to content

delay(0.seconds) does not check for cancellation #3373

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

Closed
dziemborowicz opened this issue Jul 19, 2022 · 2 comments
Closed

delay(0.seconds) does not check for cancellation #3373

dziemborowicz opened this issue Jul 19, 2022 · 2 comments
Labels
docs KDoc and API reference

Comments

@dziemborowicz
Copy link

The delay function claims to provide a prompt cancellation guarantee, but, if the duration passed to delay is 0, the function simply returns without doing anything and, in particular, without checking for cancellation.

Should this function be updated to check for cancellation even in the case where it does not suspend so that it guarantees that it never returns successfully if the job was cancelled before it was called?

Or, if not, should the documentation here be updated to note that delay does not check for cancellation if the duration is not strictly positive?

@qwwdfsad
Copy link
Collaborator

qwwdfsad commented Jul 19, 2022

delay behaves exactly according to its documentation:

There is a prompt cancellation guarantee If the job was cancelled while this function was suspended, it will not resume successfully

This is the default behaviour of most of our primitives -- cancellation is "checked" only during actual suspension for the sake of performance.

Could you please elaborate on whether you spotted it accidentally or if there was an actual production-driven use-case for that?

@dziemborowicz
Copy link
Author

You're right that the documentation says that it only checks for cancellation if it actually suspends. And it makes some sense that it doesn't suspend if the delay is 0, but this second fact isn't documented explicitly.

I shot myself in the foot with this because I hadn't considered the possibility that it wouldn't check for cancellation in the 0 case. I had some utility that would execute something repeatedly while in a certain state. In simplified terms --

fun every(duration: Duration, action: () -> Unit) {
  stateScope.launch {
    while (true) {
      delay(duration)
      action()
    }
  }
}

The idea was that when the state was no longer current, stateScope would be cancelled elsewhere and the launched coroutine would stop. If the caller passed in 0 for the duration, however, the coroutine would run forever since nothing checked for cancellation. (Trivially fixable with an isActive check of course. It was caught by unit tests.)

This has similar tones to the special cases that used to be a problem with JavaScript's setTimeout(..., 0). Documenting the special 0 case here (or removing the special case?) might save someone else's foot.

@qwwdfsad qwwdfsad added docs KDoc and API reference and removed question labels Jul 23, 2022
qwwdfsad added a commit that referenced this issue Sep 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs KDoc and API reference
Projects
None yet
Development

No branches or pull requests

2 participants