Skip to content

Commit

Permalink
Naming and Class Conventions
Browse files Browse the repository at this point in the history
- make concrete implementations final as extending them is dangerous (use composition and implement Subscription instead)
- deprecated long get/setSubscription methods in favor of short verbs (add/get/set/clear/remove)
- updated unit tests with changes
  • Loading branch information
benjchristensen committed Dec 23, 2013
1 parent 996e78f commit 813988b
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
* @see <a href="http://msdn.microsoft.com/en-us/library/system.reactive.disposables.booleandisposable(v=vs.103).aspx">Rx.Net equivalent BooleanDisposable</a>
*/
public class BooleanSubscription implements Subscription {
public final class BooleanSubscription implements Subscription {

private final AtomicBoolean unsubscribed = new AtomicBoolean(false);
private final Action0 action;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
*
* @see <a href="http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable(v=vs.103).aspx">Rx.Net equivalent CompositeDisposable</a>
*/
public class CompositeSubscription implements Subscription {
public final class CompositeSubscription implements Subscription {

private final AtomicReference<State> state = new AtomicReference<State>();

Expand Down Expand Up @@ -75,64 +75,62 @@ public boolean isUnsubscribed() {
}

public void add(final Subscription s) {
State current;
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
s.unsubscribe();
return;
} else {
newState = current.add(s);
newState = oldState.add(s);
}
} while (!state.compareAndSet(current, newState));
} while (!state.compareAndSet(oldState, newState));
}

public void remove(final Subscription s) {
State current;
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
return;
} else {
newState = current.remove(s);
newState = oldState.remove(s);
}
} while (!state.compareAndSet(current, newState));
} while (!state.compareAndSet(oldState, newState));
// if we removed successfully we then need to call unsubscribe on it
s.unsubscribe();
}

public void clear() {
State current;
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
return;
} else {
newState = current.clear();
newState = oldState.clear();
}
} while (!state.compareAndSet(current, newState));
} while (!state.compareAndSet(oldState, newState));
// if we cleared successfully we then need to call unsubscribe on all previous
// current is now "previous"
unsubscribeFromAll(current.subscriptions);
unsubscribeFromAll(oldState.subscriptions);
}

@Override
public void unsubscribe() {
State current;
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
return;
} else {
newState = current.unsubscribe();
newState = oldState.unsubscribe();
}
} while (!state.compareAndSet(current, newState));
// current is now "previous"
unsubscribeFromAll(current.subscriptions);
} while (!state.compareAndSet(oldState, newState));
unsubscribeFromAll(oldState.subscriptions);
}

private static void unsubscribeFromAll(Collection<Subscription> subscriptions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
*
* @see <a href='http://msdn.microsoft.com/en-us/library/system.reactive.disposables.refcountdisposable.aspx'>MSDN RefCountDisposable</a>
*/
public class RefCountSubscription implements Subscription {
/** The reference to the actual subscription. */
public final class RefCountSubscription implements Subscription {
private final Subscription actual;
/** Counts the number of subscriptions (1 parent + multiple children) */
private final AtomicReference<State> state = new AtomicReference<State>(new State(false, 0));

private static final class State {
Expand Down Expand Up @@ -67,20 +65,25 @@ public RefCountSubscription(Subscription s) {
this.actual = s;
}

@Deprecated
public Subscription getSubscription() {
return get();
}

/**
* Returns a new sub-subscription.
*/
public Subscription getSubscription() {
State current;
public Subscription get() {
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
return Subscriptions.empty();
} else {
newState = current.addChild();
newState = oldState.addChild();
}
} while (!state.compareAndSet(current, newState));
} while (!state.compareAndSet(oldState, newState));

return new InnerSubscription();
}
Expand All @@ -94,15 +97,15 @@ public boolean isUnsubscribed() {

@Override
public void unsubscribe() {
State current;
State oldState;
State newState;
do {
current = state.get();
if (current.isUnsubscribed) {
oldState = state.get();
if (oldState.isUnsubscribed) {
return;
}
newState = current.unsubscribe();
} while (!state.compareAndSet(current, newState));
newState = oldState.unsubscribe();
} while (!state.compareAndSet(oldState, newState));
unsubscribeActualIfApplicable(newState);
}

Expand All @@ -113,18 +116,18 @@ private void unsubscribeActualIfApplicable(State state) {
}

/** The individual sub-subscriptions. */
class InnerSubscription implements Subscription {
private final class InnerSubscription implements Subscription {
final AtomicBoolean innerDone = new AtomicBoolean();

@Override
public void unsubscribe() {
if (innerDone.compareAndSet(false, true)) {
State current;
State oldState;
State newState;
do {
current = state.get();
newState = current.removeChild();
} while (!state.compareAndSet(current, newState));
oldState = state.get();
newState = oldState.removeChild();
} while (!state.compareAndSet(oldState, newState));
unsubscribeActualIfApplicable(newState);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
* Helper methods and utilities for creating and working with {@link Subscription} objects
*/
public class Subscriptions {
public final class Subscriptions {
/**
* A {@link Subscription} that does nothing.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,54 +15,66 @@
*/
package rx.subscriptions;

import static org.mockito.Mockito.*;
import static rx.subscriptions.Subscriptions.*;
import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import rx.Subscription;
import static rx.subscriptions.Subscriptions.create;
import rx.util.functions.Action0;

public class MultipleAssignmentSubscriptionTest {

Action0 unsubscribe;
Subscription s;

@Before
public void before() {
unsubscribe = mock(Action0.class);
s = create(unsubscribe);
unsubscribe = mock(Action0.class);
s = create(unsubscribe);
}

@Test
public void testNoUnsubscribeWhenReplaced() {
MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription();
mas.setSubscription(s);
mas.setSubscription(null);
mas.unsubscribe();

mas.set(s);
mas.set(Subscriptions.empty());
mas.unsubscribe();

verify(unsubscribe, never()).call();

}

@Test
public void testUnsubscribeWhenParentUnsubscribes() {
MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription();
mas.setSubscription(s);
mas.set(s);
mas.unsubscribe();
mas.unsubscribe();

verify(unsubscribe, times(1)).call();

Assert.assertEquals(true, mas.isUnsubscribed());
}

@Test
public void testUnsubscribedDoesntLeakSentinel() {
public void subscribingWhenUnsubscribedCausesImmediateUnsubscription() {
MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription();
mas.unsubscribe();
Subscription underlying = mock(Subscription.class);
mas.set(underlying);
verify(underlying).unsubscribe();
}

mas.setSubscription(s);
@Test
public void testSubscriptionRemainsAfterUnsubscribe() {
MultipleAssignmentSubscription mas = new MultipleAssignmentSubscription();

mas.set(s);
mas.unsubscribe();
Assert.assertEquals(true, mas.getSubscription() == Subscriptions.empty());

Assert.assertEquals(true, mas.get() == s);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public void testImmediateUnsubscribe() {
public void testRCSUnsubscribeBeforeClient() {
InOrder inOrder = inOrder(main);

Subscription s = rcs.getSubscription();
Subscription s = rcs.get();

rcs.unsubscribe();

Expand All @@ -67,8 +67,8 @@ public void testRCSUnsubscribeBeforeClient() {
public void testMultipleClientsUnsubscribeFirst() {
InOrder inOrder = inOrder(main);

Subscription s1 = rcs.getSubscription();
Subscription s2 = rcs.getSubscription();
Subscription s1 = rcs.get();
Subscription s2 = rcs.get();

s1.unsubscribe();
inOrder.verify(main, never()).call();
Expand All @@ -88,8 +88,8 @@ public void testMultipleClientsUnsubscribeFirst() {
public void testMultipleClientsMainUnsubscribeFirst() {
InOrder inOrder = inOrder(main);

Subscription s1 = rcs.getSubscription();
Subscription s2 = rcs.getSubscription();
Subscription s1 = rcs.get();
Subscription s2 = rcs.get();

rcs.unsubscribe();
inOrder.verify(main, never()).call();
Expand Down
Loading

0 comments on commit 813988b

Please sign in to comment.