Skip to content
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

Remove race condition in Scheduler.effect.delayCancellable #1078

Merged
merged 1 commit into from
Feb 2, 2018

Conversation

SystemFw
Copy link
Collaborator

@SystemFw SystemFw commented Feb 2, 2018

Fixes #1077

Scheduler.effect.timedCancellable schedules an action that completes a Promise with Some(a) after a timer completes, and returns the result of reading the Promise along with an action that cancel s the timer and completes the Promise with None.
This means that if cancel is called after the timer and the action have completed, it will try to complete the Promise with None after the action has already completed it with Some(a), and therefore the cancel action will fail with a PromiseAlreadyCompletedException.

If you look at timedGet (or timedDecrementBy in Semaphore, which has the same problem), we can see where the problem is:

cancellableGet.flatMap {
      case (g, cancelGet) =>
        scheduler.effect.delayCancellable(F.unit, timeout).flatMap {
          case (timer, cancelTimer) =>
            fs2.async
              .race(g, timer)
              .flatMap(_.fold(a => cancelTimer.as(Some(a)), _ => cancelGet.as(None)))
        }
    }

If g wins the race, then cancelTimer.as(Some(a)) is executed, but if in the meantime timer has completed (so it lost the race, but it completed before cancelTimer executes), then cancelTimer.as(Some(a)) will be a failed F as explained above, and therefore it will short circuit over the as(Some(a)) and result in the whole timedGet failing with a PromiseAlreadyCompleted exception.

The fix is to make sure that cancelTimer is a no op if called after the timer has already completed, instead of having it fail.

@SystemFw
Copy link
Collaborator Author

SystemFw commented Feb 2, 2018

There's a failure in a doctest for merge which, while involving a Scheduler, seems to be unrelated (I don't think anyhing in the call chain there is calling delayCancellable)

@mpilquist mpilquist merged commit 497a5d3 into typelevel:series/0.10 Feb 2, 2018
@mpilquist
Copy link
Member

Good find!

@ChristopherDavenport
Copy link
Member

Amazing job closing this one out so quickly. Thank you very much for taking care of this!

@SystemFw
Copy link
Collaborator Author

SystemFw commented Feb 2, 2018

My pleasure :)

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

Successfully merging this pull request may close these issues.

3 participants