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

Fix of ShareReplay operator for sequential run #120

Conversation

troupmar
Copy link
Contributor

@troupmar troupmar commented Mar 22, 2022

PROBLEM (Open issue: #115)

The share(replay:) operator freezes when the upstream publisher runs sequentially.
Following operations are called in this order:

  1. receive(subscriber:) - replays buffer to the subscriber. At this point the buffer is empty since it has not received any values from the upstream publisher yet.
  2. The sequential upstream sends its values. If Just("random-value") is used as an upstream publisher, it emits single value and finished event. The subject receives those via send(_:) and send(completion:) and forwards them to the subscriber. At this time request(demand:) has not yet been called on the subscriber. Demand is zero and thus the values are not received by the subscriber.
  3. forwardCompletionToBuffer(_:) is called on subscriber after send(completion:) is received on the subject. It calls cancel() which cancels the subscriber. The subscriber does not receive any values and finishes prematurely.

FIX

The replaying of the values happen only after the request(demand:) is called on the subscriber and thus the subscriber is ready to receive values.
The cancel() on the subscriber is called only after the request(demand:) is called on the subscriber and thus the subscriber actually has a chance to receive values before being cancelled.

@@ -41,17 +41,17 @@ public final class ReplaySubject<Output, Failure: Error>: Subject {
let subscriptions: [Subscription<AnySubscriber<Output, Failure>>]

do {
lock.lock()
defer { lock.unlock() }
lock.lock()
Copy link
Member

Choose a reason for hiding this comment

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

can you undo all of the re-formatting you did here please?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can do that. But it seems you are using Indent 4 everywhere and if you look into this file in the main branch, you will see that just this do scope has Indent 2. Let me know if that is what you want and I can change it back ;)

@freak4pc
Copy link
Member

Is this related to this in your opinion ?
https://github.com/CombineCommunity/CombineExt/pull/109/files

@troupmar
Copy link
Contributor Author

troupmar commented Apr 5, 2022

Is this related to this in your opinion ?
https://github.com/CombineCommunity/CombineExt/pull/109/files

Yes, I think the previously merged change (#86) caused one of the problems I am trying to fix here. However it is just a small part of the overall problem that I consider a bug and present the fix for here.

I do not know the complete logic behind the #109 but I have tried to apply the presented changes and revert my logic with isActive within the Subscription. It seems my test is still passing, as well as the other ones, so as far as I am concerned it is fine. If you decide to merge the #109, I can rebase my MR appropriately.

@fabianmuecke
Copy link

I stumbled upon this PR, when I found my unit tests not working because of the share(replay:) operator. I switched to the PR's branch and tried it. Now my unit tests work, but I get an infinite recursion (and therefore crash) when running my app. I'll try to find out, what's going on. Just wanted to let you know, that maybe there's still a problem with this.

@troupmar
Copy link
Contributor Author

troupmar commented Apr 8, 2022

I stumbled upon this PR, when I found my unit tests not working because of the share(replay:) operator. I switched to the PR's branch and tried it. Now my unit tests work, but I get an infinite recursion (and therefore crash) when running my app. I'll try to find out, what's going on. Just wanted to let you know, that maybe there's still a problem with this.

Hi, thanks for letting me know! We use the operator at some limited places and it seems to be working just fine. It would be cool if you managed to write a test that simulates the problem. Let me know if there is any progress on that ;)

@fabianmuecke
Copy link

I haven't been able to come up with a test case, which reproduces our runtime issue, yet. It's somehow related to the Multicast triggering request(demand:) again synchronously, when receiving input, so the replay you added in that function gets called again, which calls receive(input:) on the Multicast, which in turn calls request(demand:) again and so on...

I noticed, the linked PR https://github.com/CombineCommunity/CombineExt/pull/109/files solves the same issue, this PR solves, without causing our runtime issues. The cause of the problem seems to be, that the demandBuffer gets cleared on completion. The other PR 109 fixes that, by not calling cancel. As request(demand:) does not call replay there, there's no infinite recursion. The test case you added, works on that other branch, too. From what I figured, PR 109 has not yet been merged, because it is missing a test case. So maybe, if your test case was added there, it could be merged, rendering this PR obsolete?

@mRs-
Copy link

mRs- commented Apr 11, 2022

Hey there, thanks for the contribution for CombineExt!

From my understanding we should do a joint venture between #109 and #120. The Implementation from #109 looks more simple and from my understanding it would be better to not to clear the demandBuffer.

My Idea would be to use the implementation of #109 and the Tests from #120

@freak4pc
Copy link
Member

I agree with @mRs- that @jabeattie's solution is simpler. If your test suite passes for that PR it might be better to accept a more succinct change here unless there's a reason not to.

@troupmar
Copy link
Contributor Author

@fabianmuecke @mRs- @freak4pc Yes, I agree. I asked @jabeattie to add the unit test in his PR.

@mRs-
Copy link

mRs- commented Apr 21, 2022

I've created #124

@freak4pc
Copy link
Member

I'm closing this in favor of the merged #124.
Thanks all!

@freak4pc freak4pc closed this May 12, 2022
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.

4 participants