-
Notifications
You must be signed in to change notification settings - Fork 12
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
Subscription request and cancel must enqueue synchronously #29
Comments
Yeah, I've tried to wrap my head around the |
I fixed it like this: rossabaker@c1ec229. Request and cancel have to decrement the semaphore to enqueue and increment it to release the lock for the next. A semaphore felt like a heavy hammer, and I was happy to see my tests pass without it, but I just got (un)lucky. |
Didn't notice that you removed the fix again :) |
Are you sure that fixes it? For me, it reduces the occurrences a great deal, but doesn't completely eradicate the failure (I'm running in a for loop to test). A semaphore will ensure that the calls can't happen concurrently, but it doesn't ensure that they will happen in the correct order. |
Yes, you're right. My patch is not sufficient. |
It would work provided the semaphore was decremented synchronously. def cancel(): Unit = {
semaphore.decrement.unsafeRunSync()
F.runAsync(requests.enqueue1(Cancelled) >>
semaphore.increment.liftIO[F])(_ => IO.unit).unsafeRunSync
} |
The async behaviour of a It's possible to ensure a cancellation is synchronous by using a val flag: Ref[F, Boolean] = ???
def cancel(): Unit = {
F.runAsync(flag.setSyncPure(true) >>
requests.enqueue1(Cancelled)
)(_ => IO.unit).unsafeRunSync()
}
def request(n: Long): Unit = {
F.runAsync(flag.get >>= (cancelled =>
if(cancelled) F.pure(())
else requests.enqueue1(...)
))(_ => IO.unit).unsafeRunSync
} Getting the flag is async, but an async get after a synchronous set will always return the correct value. So for the following code: subscription.cancel()
subscription.request(1)
subscription.request(2)
|
This has been fixed in release |
👍 I like this solution. It works and the code is descriptive of the problem. |
I need to make a few changes in fs2 as discussed here before backporting. It should be fairly straightforward. |
As discussed in #31 , the fs2 changes were unnecessary. I've merged in the backport and will release v0.1.1 shortly. |
This has now been released in v0.1.1:tada: |
I've just realised that I've spent a whole afternoon (during the subscription refactor) to understand a problem that was fully explained here 😞 |
Sorry about that. The test failure was familiar, but I didn't see the problem in your code. I should have pursued the history. |
nah it's fine :) |
My mistake - I should’ve rembered this one and prompted you, it took me a while to figure out last time, but it slipped my mind 😞 |
This surfaced in #28 .
According to the Spec, calls to
request
aftercancel
must be noops.However, the code for the two calls executes asynchronously. It's entirely possible for the calls to occur out of order, and thus for the subscriber to receive unwanted messages:
The
unsafeRunSync
(I think) doesn't cause the endIO
to be run synchronously.The text was updated successfully, but these errors were encountered: