-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
Use lock free strategy for several Subscription implementations #568
Conversation
Reduce contention by using CAS (Compare And Swap) operations to replace subscription
RxJava-pull-requests #497 SUCCESS |
Can you use 4-space indent to make this modification more clear? It's better that keeping consistent. |
if (unsubscribed.get()) { | ||
subscription.unsubscribe(); | ||
} else { | ||
reference.getAndSet(subscription == null ? empty() : subscription).unsubscribe(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why need to check subscription == null
? I think subscription should not be null.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assume that we have two threads, one is calling unsubscribe
, another is calling setSubscription
. If the order is line 43 37 38 46, the subscription sent to setSubscription
will not be unsubscribed.
Right, there is a potential race condition. I'm going to try to reproduce it via a unit test and find an alternative. |
I checked nullity because it was checked by the previous code. As no unit test was covering nullity cases, i chose to keep it compatible with null subscription. Do you suggest not to check for nullity ? |
You need something like this: public void setSubscription(final Subscription subscription) {
Subscription q = null;
do {
Subscription r = reference.get();
if (r == SENTINEL) {
q = newReg;
break;
}
if (reference.compareAndSet(r, subscription)) {
q = r;
break;
}
} while (true);
if (q != null) {
q.unsubscribe();
}
} Similar to this. |
What about this version using a read/write lock to minimize contention between concurrent setSubscription() calls ? |
RxJava-pull-requests #498 SUCCESS |
Thanks to akarnokd, i could get ride of the AtomicBoolean and Read/Write lock using a sentinel. |
RxJava-pull-requests #499 SUCCESS |
RxJava-pull-requests #501 FAILURE |
RxJava-pull-requests #502 SUCCESS |
RxJava-pull-requests #503 SUCCESS |
We should add isUnsubscribed to SerialSubscription |
Not sure if I understand the role of s.unsubscribe()
|
Because we need a way to indicate completion of the subscription and empty is public so clients might swap it in or out, reactivating the subscription. My example matches the Rx.Net way. |
Don't get what you say. If you are unsubscribed anyway, why not return UNSUBSCRIBED? |
Sorry, I couldn't follow jloisel's commits due to the heavy rewriting. The field The The correct class should look like this: public class SerialSubscription implements Subscription {
private final AtomicReference<Subscription> reference = new AtomicReference<Subscription>();
private static final Subscription UNSUBSCRIBED = new Subscription() {
@Override
public void unsubscribe() {
}
};
@Override
public void unsubscribe() {
Subscription q = reference.getAndSet(UNSUBSCRIBED);
if (q != null) {
q.unsubscribe();
}
}
public void setSubscription(final Subscription subscription) {
Subscription q = null;
do {
final Subscription current = reference.get();
if (current == UNSUBSCRIBED) {
q = subscription;
break;
}
if (reference.compareAndSet(current, subscription)) {
q = current;
break;
}
} while (true);
if (q != null) {
q.unsubscribe();
}
}
public Subscription getSubscription() {
Subscription subscription = reference.get();
return subscription == UNSUBSCRIBED ? Subscriptions.empty() : subscription;
}
public boolean isUnsubscribed() {
return reference.get() == UNSUBSCRIBED;
}
} |
headinthebox > UNSUBSCRIBED is the internal sentinel. It should not escape from SerialSubscription internal implementation, since it could lead to unexpected behavior. We could another complete different approach: since managing a thread-safe mutable state is difficult, shouldn't we make the serial subscription immutable ? I mean:
And remove getSubscription() since it's never used in the api. Of course, this tends to no backward compatibility on this class, since it's public. |
If we tend to have the same behavior as before, getSubscription() should return null when previously unscribed. |
RxJava-pull-requests #505 SUCCESS |
I think we cannot use Subscriptions.empty() as unsubscribed sentinel, since it would behave unexpectedly it one sets empty() from outside via setSubscription(). I tend also to avoid null references in implementation to avoid unnecessary burden which reduces comprehension. rx-core code base analysis shows that SerialSubscription could be easily immutable. But it diverges with Rx contract: Rather than try to have a complex contract for SerialSubscription, i would give a try to make it immutable. Immutable is thread-safe by nature as well as contention free. |
Oh, it seems like due to cyclic dependencies (like on ResultSink), it's not possible to make it immutable. |
RxJava-pull-requests #525 SUCCESS |
Looks like an improvement on current implementation and don't see problems. Using a state machine here is far preferable to the previous lock based implementation. This should help or fix #577 |
Use lock free strategy for several Subscription implementations
Use lock free strategy for several Subscription implementations
…e exception so that it is also possible to count exceptions as a success. (ReactiveX#573) The list of ignored exceptions has always precedence. If an exception is ignored it neither counts as a success nor failure. If the list of recorded exceptions only contains some exceptions, all others count as a success, unless they are not part of the list of ignored exceptions.
Reduce contention by using CAS (Compare And Swap) operations to replace
subscription in several subscription implementations.