-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Subscription and Synchronization #1383
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
Comments
Please take a look at an exploration of this in #1384 |
It makes a lot of sense to make the same assumptions for subscribers and observers, and only synchronize in operators like One question, is there something like Would you be able to release this as a canary in production? |
I have created a 'SynchronizedSubscription' for internal use right now. We also have the 'unsubscribeOn' operator, but I have not proven to myself yet that it is all 100% correct. |
Yes, I could canary this, but the types of issues that this could cause probably would not be easily seen in our environment since it is still mostly request/response and an unsubscribe being missed wouldn't severely affect anything (it would just get filtered out by take for example). Where we do use streams they are infinite and thus never exercise the unsubscribe. |
Makes sense. Schedulers are another place where both subscriptions and concurrency exist. |
Yes I'll need to review those. |
I've been spending more time on this and it definitely appears to consistently help performance. The one area where I think this change makes us vulnerable is in this signature: public Subscription subscribe(Subscriber s)
public Subscription subscribe(Observer s) I propose changing these to: public void subscribe(Subscriber s)
public void subscribe(Observer s) Returning the Is there any reason (other than being a bad breaking change) for keeping the Can we keep them without impeding performance? |
Mmmm, need to think about this. I do unsubscribe when I subscribe quite often, and it will be a massive breaking change. Since most operators are defined using |
I tend to save all current subscriptions in my Android apps on the Activity, then when the Activity is destroyed, it unsubscribes from them all in order to free up resources. It gets used extensively there. I think it is required because if the Subscription holds a reference to the Activity (via onNext, etc), then the Activity may never be deallocated. I have an app that has a long lasting TCP connection pumping events into a hot Observable that survives multiple Activities and if I could not unsubscribe from it, then those Activities would be leaked. It would require using WeakReferences to the activity inside of subscriptions to overcome I think. That was used in the Android library previously, but was removed because it caused other problems (I cannot recall what they were). |
@DylanSale Thanks for helping on this. I'm pretty certain we can't do this change as it is a very significant breaking change for this late in the game. Putting that aside, on your use case, would it work if you use someObservable.takeUntil(activityDestroyed).subscribe(s)
... or ...
someObservable.takeWhile(activityIsActive).subscribe(s) |
Yes, it looks like that would work. Cheers for that! |
I did some further testing on different approaches and here are the results (manually formatted to make it more readable while comparing):
Considering it as unlikely that we can make the necessary changes to allow a Unless we make a design change that removes direct use of It can not be hidden inside something like a Does anyone disagree with my assessment and testing above? Is there a better alternative I'm not considering? (I can give the raw JMH results if interested. And sorry about the wrong post that I posted then deleted ... and the closing/reopening of this issue ... fat fingered it). |
By the way, the tests I included above show two different perspectives of RxJava. The The results show The point of this issue is to present and discuss the impact of requiring a thread-safe Ideally, if I could go back in time, I think I would change the signature of |
As per discussion in ReactiveX#1383 ... despite the performance hit.
I want to explore whether it is possible to eliminate synchronization inside a
Subscription
, particularly theSubscriptionList
used insideSubscriber
.We have discussed this before and determined that it is difficult to guarantee it doesn't get called from other threads when
subscribeOn
andobserveOn
are used, since a user can get a reference to theSubscription
and callunsubscribe
from anywhere.The cost however of protecting against this slim chance is pretty high. The
rx.PerfBaseline.observableConsumption
perf test shows that by removing synchronization (including use of volatile and/or cas) we can increase from ~32m to ~46m ops/second.Since this is a non-trivial increase, I'd like to determine if we can adopt a single-threaded assumption for
Subscription
that same as we do forObserver
.The
observeOn
operator does schedule theunsubscribe
for the worker: https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/internal/operators/OperatorObserveOn.java#L190 But it passes the sameSubscriber
through to the parent (https://github.com/Netflix/RxJava/blob/master/rxjava-core/src/main/java/rx/internal/operators/OperatorObserveOn.java#L75) which means it is crossing over thread boundaries. This is an example of an area whereSubscription
requires synchronization as currently used.Similarly, the
subscribeOn
use case can mean a user gets the returnedSubscription
on one thread while the work happens on a second. The user can therefore callunsubscribe
from the first thread and it needs to synchronize to the second.Most of the time though we have synchronous execution where synchronization is not needed but we always pay the cost.
Anyone else have insights on this or how we can improve so that we only pay the cost of synchronization when needed? For example, I wonder if we can fix the
observeOn
implementation, and decorate theSubscription
resulting fromsubscribeOn
to include synchronization, and leave it unsynchronized the rest of the time. Is that sufficient to be safe?The text was updated successfully, but these errors were encountered: