-
Notifications
You must be signed in to change notification settings - Fork 66
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
Fix unlawful instances #595
Conversation
@neko-kai please help with the failing tests 🙏 |
…ve typed errors in the same Cause as external interruptions, so previous definition was incorrect There remain test failures in 'canceled sequences onCancel in order' – they are occur when `genOfRace`/`genOfParallel` or `genCancel` occurs, so something might still be wrong with outcome conversion in these case OR there may be bugs in ZIO 2 (or some more tricky behavior)
@johnspade |
@neko-kai
It looks like we have to fix #562. |
@johnspade |
# Conflicts: # zio-interop-cats-tests/shared/src/test/scala/zio/interop/CatsSpecBase.scala # zio-interop-cats/shared/src/main/scala/zio/interop/package.scala
The failing case for the "monadCancel.onCancel associates over uncancelable boundary" law looks like this in pure ZIO: def canceled = {
def loopUntilInterrupted: UIO[Unit] =
ZIO.descriptorWith(d => if (d.interrupters.isEmpty) ZIO.yieldNow *> loopUntilInterrupted else ZIO.unit)
for {
_ <- ZIO.withFiberRuntime[Any, Nothing, Unit]((thisFiber, _) => thisFiber.interruptAsFork(thisFiber.id))
_ <- loopUntilInterrupted
} yield ()
}
ZIO.unit.onExit(_ => canceled *> ZIO.never) This code hangs and fails the test, but works fine with F.onCancel(F.uncancelable(_ => fa), F.canceled *> F.never)
|
@johnspade If this hangs on ZIO2 that means it works differently now - we may need to find another way to reliably check for external interrupters (in uninterruptible region). /cc @adamgfraser Or we could just give up and return Unit immediately if we're in an uninterruptible region. Edit: Actually, looking at the examples again, they should hang anyway because of |
Don't we just need to use object Example extends ZIOAppDefault {
val awaitInterruption: UIO[Any] =
ZIO.descriptorWith(d => if (d.interrupters.nonEmpty) ZIO.interrupt *> ZIO.never else awaitInterruption)
val run =
for {
promise <- Promise.make[Nothing, Unit]
fiber <- ZIO.unit.onExit(_ => promise.succeed(()) *> awaitInterruption).forkDaemon
_ <- promise.await
exit <- fiber.interrupt
_ <- Console.printLine(exit)
} yield ()
} |
Cats |
Okay, then I think we want something like: object Example extends ZIOAppDefault {
val canceled: UIO[Any] =
ZIO.fiberIdWith { fiberId =>
FiberRef.interruptedCause.update(_ ++ Cause.interrupt(fiberId))
}
val run =
for {
fiber <- ZIO.unit.onExit(_ => canceled *> ZIO.debug("Still going")).forkDaemon
exit <- fiber.await
_ <- Console.printLine(exit)
} yield ()
} |
Yeah, sorry, I got confused, this is working as intended. |
case (Succeeded(Some(_)), _) | (_, Succeeded(Some(_))) => | ||
println(s"$out1 was not equal to $out2") | ||
false | ||
case _ => true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can ignore results like Succeeded(None)
, it means that IO under tests did not produce a result (IO.never
, for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@johnspade We can't ignore it. If both lhs and rhs hang then it's fine, but not if one of the sides finishes and the other hangs. And case _ => true
here ignores everything, not just Succeeded(None)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, cats-effect uses similar relaxed Eq
for law testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, cats-effect uses similar relaxed
Eq
for law testing
I would really love to know how you came up with that, but cats-effect tests use the eqIOA
instance defined in TestInstances
- https://github.com/typelevel/cats-effect/blob/series/3.x/tests/shared/src/test/scala/cats/effect/IOSpec.scala#L1580 - which uses the Eq[Outcome[Option, Throwable, A]]
instance - i.e. out1 eqv out2
here, that was used before - which checks all the branches not just success.
implicit def eq[F[_], E: Eq, A](implicit FA: Eq[F[A]]): Eq[Outcome[F, E, A]] =
Eq instance {
case (Canceled(), Canceled()) => true
case (Errored(left), Errored(right)) => left === right
case (Succeeded(left), Succeeded(right)) => left === right
case _ => false
}
@neko-kai Please take a look if everything is okay in this PR, I can fix it in the separate PR |
@adamgfraser I've tried your implementation for |
@johnspade Okay. Is this still an issue? Can we isolate what the expected behavior is that we need to support? |
@adamgfraser No, everything is working fine now (at least according to law tests). I was wrong and my example should hang with Cats.
This is the expected behavior. |
@johnspade Okay, great! |
@adamgfraser @jdegoes @johnspade |
F.uncancelable(_ => F.onCancel(fa, fin)) <-> F.onCancel(F.uncancelable(_ => fa), fin) When the |
@johnspade |
@neko-kai I've observed such behavior in my debugging, I guess generators are randomly chosen on each |
@johnspade In that case no tests would ever pass at all, because e.g. Also, the tests also fail when using Also it works on ZIO1 branch which has about the same generators and eq instances. What you could have observed is |
@neko-kai I'm fine with reverting this until we are all comfortable with the changes here since it seems like we are still figuring this out. @johnspade I will take a look myself but did my suggestion fix the issue with this test? I know you said it caused issues but I think we need to understand what those issues are. |
This reverts commit 274fad3.
Okay, let's revert and reopen it |
This reverts commit 274fad3.
@adamgfraser No, this test (monadCancel.onCancel associates over uncancelable boundary) is still failing with your suggestions |
@johnspade Okay let's take a look at that. |
Port to ZIO 2 and apply the following fixes by @neko-kai from the
master
branch:Async#async
implementation #542MonadCancel#canceled
by sending an external interrupt to current fiber viaFiber.unsafeCurrentFiber
#544