Skip to content

withTimeout leaks exceptions caught inside it #892

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
baltiyskiy opened this issue Dec 14, 2018 · 2 comments
Closed

withTimeout leaks exceptions caught inside it #892

baltiyskiy opened this issue Dec 14, 2018 · 2 comments
Labels

Comments

@baltiyskiy
Copy link

baltiyskiy commented Dec 14, 2018

This test fails:

  @Test
  fun `withTimeout leaks caught exceptions`() {
    val start = AtomicLong()
    try {
      runBlocking {
        start.set(System.nanoTime())
        withTimeout(100) {
          while (true) {
            try {
              GlobalScope.future {
                throw FooException()
              }.await()
            } catch (ignored: FooException) {
              println("caught FooException")
            }
          }
        }
      }
    } catch (e: CancellationException) {
      // OK
    } catch (e: FooException) {
      val elapsed = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start.get())
      fail("expected CancellationException, got $e; elapsed time: ${elapsed}ms")
    }
  }

  class FooException : Exception("foo")

Output:

caught FooException (169 matches)
java.lang.AssertionError: expected CancellationException, got FooException: foo; elapsed time: 112ms

I would never expect withTimeout to throw FooException, since it is caught inside it!

@baltiyskiy
Copy link
Author

Note: GlobalScope.future is used only for presentation --- in reality, it is a j.u.concurrent.Future provided by a third-party library.

@elizarov
Copy link
Contributor

Thanks. It has the same underlying problem as discussed in #830. I've confirmed it by applying Workaround#1 to the implementation of await() and it did fix the problem. Let's keep this bug open as a separate issue to make sure we don't forget to fix await().

@elizarov elizarov added the bug label Dec 14, 2018
elizarov added a commit that referenced this issue Dec 15, 2018
There seems to be no other way to satisfactory fix the problem of
a race between cancellation on CancellationContinuation and
concurrent failure of the corresponding background process in a
way the distinguishes "failure from cancellation attempt" from
a "true failure" without placing undue burden on anyone who is
implementing `await()` extension function for various future types.

- Added testTimeoutCancellationFailRace that works as a perfect litmus
  test; being allowed to run for a while it realiable detects various
  problems in alternative approaches.
- Optimized CancellableContinuationImpl; merged its code with
  AbstractContinuation class that is not needed as a separate
  class and it removed.
- Deprecated and hidden CancellableContinuation.initCancellability();
  it is now always invoked after the end of the block that was
  passed to suspendCancellableCoroutine.
- Deprecated `holdCancellability` parameter to an internal
  suspendAtomicCancellableCoroutine function.

Fixes #892
Fixes #830
elizarov added a commit that referenced this issue Dec 15, 2018
There seems to be no other way to satisfactory fix the problem of
a race between cancellation on CancellationContinuation and
concurrent failure of the corresponding background process in a
way the distinguishes "failure from cancellation attempt" from
a "true failure" without placing undue burden on anyone who is
implementing `await()` extension function for various future types.

- Added testTimeoutCancellationFailRace that works as a perfect litmus
  test; being allowed to run for a while it realiable detects various
  problems in alternative approaches.
- Optimized CancellableContinuationImpl; merged its code with
  AbstractContinuation class that is not needed as a separate
  class and it removed.
- Deprecated and hidden CancellableContinuation.initCancellability();
  it is now always invoked after the end of the block that was
  passed to suspendCancellableCoroutine.
- Deprecated `holdCancellability` parameter to an internal
  suspendAtomicCancellableCoroutine function.

Fixes #892
Fixes #830
elizarov added a commit that referenced this issue Dec 17, 2018
There seems to be no other way to satisfactory fix the problem of
a race between cancellation on CancellationContinuation and
concurrent failure of the corresponding background process in a
way the distinguishes "failure from cancellation attempt" from
a "true failure" without placing undue burden on anyone who is
implementing `await()` extension function for various future types.

- Added testTimeoutCancellationFailRace that works as a perfect litmus
  test; being allowed to run for a while it reliably detects various
  problems in alternative approaches.
- Optimized CancellableContinuationImpl; merged its code with
  AbstractContinuation class that is not needed as a separate
  class and removed.
- Deprecated and hidden CancellableContinuation.initCancellability();
  it is now always invoked after the end of the block that was
  passed to suspendCancellableCoroutine.
- Deprecated `holdCancellability` parameter to an internal
  suspendAtomicCancellableCoroutine function.

Fixes #892
Fixes #830
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants