-
Notifications
You must be signed in to change notification settings - Fork 24
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
Race condition with ReconnectableObservable #2
Comments
Interesting, thanks for reporting this. I haven't thought it through fully yet, but I can see how there may be a race condition. Perhaps the simplest solution is to dispose of the source subscription within the gate? |
I think that would be a valid improvement but I'm not sure it fixes the issue. I've changed the code and tested it to be sure. It's a lot more reliable now but there is still a race I believe. I can imagine a scenario where you call dispose which is then scheduled, following this control returns and a new subscriber gets returned the existing subject, then the scheduled dispose runs and nulls out the subscription. Does that sound plausible? Albeit in the case of my test code it's using Scheduler.Default so I wouldn't have thought the above could happen. Presumably subscribers would need to be using different schedulers for this to be the case. I guess that means something else is happening. |
Hmm I see, it definitely sounds plausible. I wonder whether it's an issue with the operator though or just a higher-level problem. In the problem case, user code calls Consider an attempt at a solution to the problem in this operator:
Therefore, this seems like a higher-level problem to me. Am I missing anything? |
I agree with you in regards to points 1-5. I still think it is an issue with the operator though. I'm not sure what you mean by higher-level problem. The behavior is not one you would expect worse still it appears to the final subscriber as if they are subscribed but just moments later is disposed. If the original hot sequence that was being multicast has subscription side effects like creating a new socket connection these also get executed after the disposal. This new hot sequence is then never subscribed to. I can think of 2 options for solving this potentially.
I think I'm still missing something though. Given the .Multicast sequence is using a single scheduler why could they be out of order. Is it just because of the Subject vs the _source. So we have subscribed to the _source on the same scheduler but the Subject is on the current thread at the time that Subscribe is called. That's what led me to try option 1. Let me know what you think. |
What I meant by "higher level problem" is that it's not the operator's fault that it doesn't avoid race conditions between Requiring an I believe it's an edge case, because using Your point about using the same This is potentially a problem with any operator that cares about the order in which |
I just examined your code sample again and noticed that you're applying the I suspect that Edit: To be clear, the reason is because when you subscribe to |
hmm... I hadn't ever considered SubscribeOn to be an anti-pattern. The logic here is the work inside the subscribe operator might take a little time so you want to offload this to the threadpool. It's not a use case for ObserveOn. That said I wasn't aware that 2 independent subscriptions could be running simultaneously when using SubscribeOn with the same scheduler. I thought things where run in the order they are scheduled hence any call to .Dispose would be scheduler to occur before the next Subscribe in this case. Consider the use case were this multicast observable is exposed via a service IService. This service is injected into 2 view controllers that independently subscribe and dispose exactly as my test code does. How would you solve this race condition? I don't believe you'd even be aware of it. No other RX operator springs to mind that would exhibit this behavior. Hence I think it must be solved in the operator itself. Just my opinion and happy to be convinced otherwise. |
How could it be solved in the operator? Using an |
|
Take a look at the public static IObservable<TSource> SubscribeOn<TSource>(IObservable<TSource> source, IScheduler scheduler)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (scheduler == null)
{
throw new ArgumentNullException("scheduler");
}
return new AnonymousObservable<TSource>(delegate(IObserver<TSource> observer)
{
SingleAssignmentDisposable singleAssignmentDisposable = new SingleAssignmentDisposable();
SerialDisposable d = new SerialDisposable();
d.Disposable = singleAssignmentDisposable;
singleAssignmentDisposable.Disposable = scheduler.Schedule(delegate
{
d.Disposable = new ScheduledDisposable(scheduler, source.SubscribeSafe(observer));
});
return d;
});
} |
Hmm, now I'm thinking that this isn't your problem to begin with.
This couldn't be the case because |
Well, to be clear, the order in which |
Perhaps it's the following sequence of events that's causing the issue.
Therefore, perhaps |
You could prove my hypothesis by creating a new version of |
Hi Dave,
I believe there is a race condition in the logic for Disposable versus new subscribers. I can't seem to reproduce it with a unit test using TestSchedulers. I can though using more realistic code (see attached), albeit not every time. What seems to happen is the RefCount drops to zero and the disposable is scheduled but not executed, subsequently a new subscriber comes in and gets the existing subject, then the subject is disposed beneath them and no more updates come though.
It only happens when you have a .SubscribeOn
Wondered if you had any thoughts on this?
Crude test code
CrudeTestCode.txt
The text was updated successfully, but these errors were encountered: