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

Introduce right-biased race variant #3233

Closed

Conversation

armanbilge
Copy link
Member

@armanbilge armanbilge commented Nov 7, 2022

Erm, I just realized that the current naming is a bit unfortunate. So let's bikeshed that.

Based on #3232. I suggested this idea on Discord and Fabio said "interesting (edited)" so I knew I was onto something 😇

The basic idea is to introduce a variant of race that is robust to the case where the loser still completes (i.e. its outcome is not Canceled), whether due to race condition or uncancelability. The idea is to right-bias the race, so that the "blessed" fiber is always reported as the winner unless it canceled.

This sort of thing seems important when you are e.g. timing-out a queue.take or semaphore.acquire in which case if the action succeeds, you almost definitely want to know about it regardless of the timeout or not. In fact, I'd even argue that we should re-implement the timeout-based methods to be in terms of this method instead of vanilla race.

The motivating context is here:

https://github.com/typelevel/fs2/blob/dd20eb65b6cc59a923db44b21e29ec7c2e08e3a6/core/shared/src/main/scala/fs2/Stream.scala#L1442-L1460

Notice the comment about JS being "broken" which is what sent me on this rampage to begin with 😡

I had a difficult time reasoning about what exactly was going on there. Essentially a semaphore.acquireN is being timed-out. But to guard in case the acquireN succeeded despite losing the race, it is followed by a guarantee clause to releaseN. And then in the case that the acquireN actually won the race, the acquireN is actually done all over again since that was the intention all along. This all seems very convoluted to me.

@djspiewak
Copy link
Member

Another idea: what if we just unbiased race itself? Rust does something interesting here in that it randomly flips whether it tests left or right first for completion. This has the effect of injecting fairness into the race.

@armanbilge
Copy link
Member Author

I'm not sure I understand. race is already biased? The purpose of this PR is to specifically get a biased variant. I think that's essential for the cases I enumerated.

@djspiewak
Copy link
Member

race is already biased?

If both sides complete simultaneously, the left one will be chosen. :-) It's just hard to see this in practice.

I'll try to put some more time into digesting this asap.

@armanbilge
Copy link
Member Author

So concretely I'm imagining something like this:

IO.raceRightBiased(
  IO.sleep(1.second),
  IO.uncancelable(_ => IO.sleep(2.seconds).as(42))
)

should complete as Right(42).

The real use-case is when the right fiber is a queue.take or a semaphore.acquire sort of thing. Where, regardless of which fiber won the race, once the dust has settled and both fibers are either completed or canceled, preference is shown to the right fiber (unless it actually canceled).

@armanbilge
Copy link
Member Author

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.

2 participants