-
Notifications
You must be signed in to change notification settings - Fork 530
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 "inclusive" race
as new default semantic
#3456
Comments
Aren't both of those things related to the former semantic? |
In this case,
In this case, Does that answer your question? |
Sorry I didn't phrase my question very well. I was mainly confused about this part:
Isn't this an example of a possibility that the former semantic gives you, and not the latter? |
Oh, gotcha, I think you are right! Daniel can clarify maybe. |
uh, I just found this issue after I implemented my own sketchy version while my original goal was to achieve a similar short-circuit evaluation semantic of boolean for IO racing but the general concern about the semantic seems very valid, nice to see this API came into cats-effect. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
`timeout*` methods are implemented in terms of a race between a desired effect and the timeout. In the case that both effects complete simultaneously, it could happen that the timeout would win the race, a `TimeoutException` be raised, and the outcome of the desired effect lost. As is noted in typelevel#3456, this is a general problem with the `race*` methods, and can't be addressed in the general case without breaking the current interfaces. This change is a more narrow take on the problem specifically focusing on the `timeout` and `timeoutTo` methods. As these methods inherently wait for both racing effects to complete, the implementation is changed to always take into account the outcome of the desired effect, only raising a `TimeoutException` if the timeout won the race *and* the desired effect was effectively canceled. Similarly, errors from the desired effect are preferentially propagated over the generic `TimeoutException`. The `timeoutAndForget` methods are left unchanged, as they explicitly avoid waiting for the losing effect to finish. This change allows for `timeout` and `timeoutTo` methods to be safely used on effects that acquire resources, such as `Semaphore.acquire`, ensuring that successful outcomes are always propagated back to the user.
Full discussion here: https://discord.com/channels/632277896739946517/839263556754472990/1078054700932419695
The short version is that
race
, in its current form, represents a resource leak. If there's a tie (i.e.f.cancel *> f.join
does not produceOutcome.Canceled()
), then it's ambiguous as to what result should be chosen. Right now, we technically bias to theLeft
, but this is just an implementation detail made manifest. Even worse, if a race is doubly-successful in this way, it's very possible that a resource was acquired which will now just disappear. In most cases, resources which are acquired and then later exposed to an error or self-cancelation will trigger finalizers, but often not in success cases, so this is particularly dangerous.In a very real sense,
race
should never have returnedEither
. The more correct return type would have beenIor
.We can address this in one of two ways. We can either introduce a new variant, like
raceInclusive
, which returnsIor
and then deprecaterace
. Alternatively, we can play tricks withDummy
implicits and break everyone's sources:I'm very tempted to say this is better because it avoids significant problems and cleans up the API (in some sense), but this is definitely a request for comments.
This is all orthogonal to the fiber isolation semantics of
race
. This affects a few things, but the most significant being self-cancelation semantics: do we treat self-cancelation as "losing the race", or do we treat it as cancelation and embed that result into the parent fiber? The former semantic leads to issues like #3396, while the latter semantic opens up some interesting possibilities (such as a computation "giving up" and allowing the alternative to complete naturally).racePair
is capable of handling all of this generality, and so for any use-cases which are not extremely common, we should push people in that direction, despite the fact that it's very low-level and somewhat hard to work with correctly. We should focus the pretty parts of the API on the most common semantics, which is likely fiber non-isolation (i.e.timeout
semantics).The text was updated successfully, but these errors were encountered: