Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@regiskuckaertz
Copy link
Member

Closes #293

I feel this version is more in line with the style we use elsewhere and also much easier to read. And I think now I understand Promise.bracket, even though I don't really need its full power (i.e. the release action).

@jdegoes
Copy link
Member

jdegoes commented Oct 16, 2018

You will probably need the release action in Promise.bracket so you can remove the promise from the Semaphore state. Because if someone calls acquire but needs to wait, then it gets interrupted, that needs to be cleaned up.


val release: (Boolean, Promise[Nothing, Unit]) => IO[Nothing, Unit] = {
case (_, p) =>
p.poll.void <> state.update {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p.poll.void can have no effect, why use it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to say the promise can only be completed if it is taken out of the queue, but interruption can occur between the complete call and the moment the queue is updated in the ref.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand. I think if you delete p.poll.void <>, the meaning of this code won't change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the happy path, the promise given to release is already completed (that is part of Promise.bracket, specifically the p.get), which can only happen if it is not in the queue (that is imposed by the definition of acquireN/releaseN).

Actually I take back my previous comment, because Promise.bracket guarantees atomicity, if the promise is completed we can deduce that it is not in the queue anymore! So, with p.poll.void we avoid the extra state update if the promise has already been removed from the queue.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does p.poll do? AFAIK, it doesn't do anything, it just peeks in the promise and returns the value as a result, which is then thrown away with void. Am I missing something?

Copy link
Member Author

@regiskuckaertz regiskuckaertz Oct 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, I'm only interested to know whether the promise is complete or not: and poll fails in the latter case, which means I can provide an alternative action (update the state) with the <> combinator.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, OK, I see now that poll fails with Unit if the result is not available. Unfortunately there's a race condition—it's possible the promise will be completed between p.poll.void and state.update. Because other threads may be concurrently modifying state (releasing, which could trigger promise completion). If there's a race condition then we should always update the state. Or am I missing something else?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release action is inside a finaliser (via io.ensuring(...)), which means it is not interruptible and so this should be fine. Amirite?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not interruptible, however, the state of the promise can still change during the execution of the action. Promise.bracket provides no guarantees on concurrent access, only a guarantee if acquire succeed, that release will succeed, regardless of interruption.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is correct 😞 Thanks for the explanation!

val release: (Boolean, Promise[Nothing, Unit]) => IO[Nothing, Unit] = {
case (_, p) =>
p.poll.void <> state.update {
case Left(q) => Left(q.filterNot(_._1 == p))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this will help ensure the promise is removed. I'd use eq instead of ==.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd have to make Promise a subtype of AnyRef, which sounds reasonable anyway?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, let's just leave it.

_ <- s.acquire
} yield () must_=== (()))
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to add one crazy test in here that does stuff from a lot of fibers. The above one is certainly helpful.

@jdegoes
Copy link
Member

jdegoes commented Oct 16, 2018

@regiskuckaertz Huge improvement! Way less code and easier to understand. I'm must more confident in its correctness. Would be good to add a test for the case of an interrupted acquire.

@jdegoes jdegoes merged commit edbb21d into zio:master Oct 19, 2018
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