1
- /**
2
- * Copyright 2013 Netflix, Inc.
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
1
+ /**
2
+ * Copyright 2013 Netflix, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
16
package rx .subscriptions ;
17
17
18
18
import static java .util .Arrays .asList ;
19
19
import static java .util .Collections .unmodifiableSet ;
20
20
21
21
import java .util .ArrayList ;
22
22
import java .util .Collection ;
23
+ import java .util .Collections ;
23
24
import java .util .HashSet ;
24
25
import java .util .Set ;
25
26
import java .util .concurrent .atomic .AtomicReference ;
30
31
/**
31
32
* Subscription that represents a group of Subscriptions that are unsubscribed
32
33
* together.
33
- *
34
+ *
34
35
* @see <a
35
36
* href="http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable(v=vs.103).aspx">Rx.Net
36
37
* equivalent CompositeDisposable</a>
37
38
*/
38
39
public class CompositeSubscription implements Subscription {
39
- private static final Set <Subscription > MUTATE_STATE = unmodifiableSet (new HashSet <Subscription >());
40
- private static final Set <Subscription > UNSUBSCRIBED_STATE = unmodifiableSet (new HashSet <Subscription >());
41
-
40
+ /** Sentinel to indicate a thread is modifying the subscription set. */
41
+ private static final Set <Subscription > MUTATE_SENTINEL = unmodifiableSet (Collections .<Subscription >emptySet ());
42
+ /** Sentinel to indicate the entire CompositeSubscription has been unsubscribed.*/
43
+ private static final Set <Subscription > UNSUBSCRIBED_SENTINEL = unmodifiableSet (Collections .<Subscription >emptySet ());
44
+ /** The reference to the set of subscriptions. */
42
45
private final AtomicReference <Set <Subscription >> reference = new AtomicReference <Set <Subscription >>();
43
-
46
+
44
47
public CompositeSubscription (final Subscription ... subscriptions ) {
45
48
reference .set (new HashSet <Subscription >(asList (subscriptions )));
46
49
}
47
-
50
+
48
51
public boolean isUnsubscribed () {
49
- return reference .get () == UNSUBSCRIBED_STATE ;
52
+ return reference .get () == UNSUBSCRIBED_SENTINEL ;
50
53
}
51
-
54
+
52
55
public void add (final Subscription s ) {
53
56
do {
54
57
final Set <Subscription > existing = reference .get ();
55
- if (existing == UNSUBSCRIBED_STATE ) {
58
+ if (existing == UNSUBSCRIBED_SENTINEL ) {
56
59
s .unsubscribe ();
57
60
break ;
58
61
}
59
-
60
- if (reference .compareAndSet (existing , MUTATE_STATE )) {
62
+
63
+ if (existing == MUTATE_SENTINEL ) {
64
+ continue ;
65
+ }
66
+
67
+ if (reference .compareAndSet (existing , MUTATE_SENTINEL )) {
61
68
existing .add (s );
62
69
reference .set (existing );
63
70
break ;
64
71
}
65
72
} while (true );
66
73
}
67
-
74
+
68
75
public void remove (final Subscription s ) {
69
76
do {
70
77
final Set <Subscription > subscriptions = reference .get ();
71
- if (subscriptions == UNSUBSCRIBED_STATE ) {
78
+ if (subscriptions == UNSUBSCRIBED_SENTINEL ) {
72
79
s .unsubscribe ();
73
80
break ;
74
81
}
75
-
76
- if (reference .compareAndSet (subscriptions , MUTATE_STATE )) {
82
+
83
+ if (subscriptions == MUTATE_SENTINEL ) {
84
+ continue ;
85
+ }
86
+
87
+ if (reference .compareAndSet (subscriptions , MUTATE_SENTINEL )) {
77
88
// also unsubscribe from it:
78
89
// http://msdn.microsoft.com/en-us/library/system.reactive.disposables.compositedisposable.remove(v=vs.103).aspx
79
90
subscriptions .remove (s );
@@ -83,54 +94,66 @@ public void remove(final Subscription s) {
83
94
}
84
95
} while (true );
85
96
}
86
-
97
+
87
98
public void clear () {
88
99
do {
89
100
final Set <Subscription > subscriptions = reference .get ();
90
- if (subscriptions == UNSUBSCRIBED_STATE ) {
101
+ if (subscriptions == UNSUBSCRIBED_SENTINEL ) {
91
102
break ;
92
103
}
93
-
94
- if (reference .compareAndSet (subscriptions , MUTATE_STATE )) {
104
+
105
+ if (subscriptions == MUTATE_SENTINEL ) {
106
+ continue ;
107
+ }
108
+
109
+ if (reference .compareAndSet (subscriptions , MUTATE_SENTINEL )) {
95
110
final Set <Subscription > copy = new HashSet <Subscription >(
96
111
subscriptions );
97
112
subscriptions .clear ();
98
113
reference .set (subscriptions );
99
-
100
- for (final Subscription subscription : copy ) {
101
- subscription .unsubscribe ();
102
- }
114
+
115
+ unsubscribeAll (copy );
103
116
break ;
104
117
}
105
118
} while (true );
106
119
}
107
-
120
+ /**
121
+ * Unsubscribe from the collection of subscriptions.
122
+ * <p>
123
+ * Exceptions thrown by any of the {@code unsubscribe()} methods are
124
+ * collected into a {@link CompositeException} and thrown once
125
+ * all unsubscriptions have been attempted.
126
+ * @param subs the collection of subscriptions
127
+ */
128
+ private void unsubscribeAll (Collection <Subscription > subs ) {
129
+ final Collection <Throwable > es = new ArrayList <Throwable >();
130
+ for (final Subscription s : subs ) {
131
+ try {
132
+ s .unsubscribe ();
133
+ } catch (final Throwable e ) {
134
+ es .add (e );
135
+ }
136
+ }
137
+ if (!es .isEmpty ()) {
138
+ throw new CompositeException (
139
+ "Failed to unsubscribe to 1 or more subscriptions." , es );
140
+ }
141
+ }
108
142
@ Override
109
143
public void unsubscribe () {
110
144
do {
111
145
final Set <Subscription > subscriptions = reference .get ();
112
- if (subscriptions == UNSUBSCRIBED_STATE ) {
146
+ if (subscriptions == UNSUBSCRIBED_SENTINEL ) {
113
147
break ;
114
148
}
115
-
116
- if (subscriptions == MUTATE_STATE ) {
149
+
150
+ if (subscriptions == MUTATE_SENTINEL ) {
117
151
continue ;
118
152
}
119
-
120
- if (reference .compareAndSet (subscriptions , UNSUBSCRIBED_STATE )) {
121
- final Collection <Throwable > es = new ArrayList <Throwable >();
122
- for (final Subscription s : subscriptions ) {
123
- try {
124
- s .unsubscribe ();
125
- } catch (final Throwable e ) {
126
- es .add (e );
127
- }
128
- }
129
- if (es .isEmpty ()) {
130
- break ;
131
- }
132
- throw new CompositeException (
133
- "Failed to unsubscribe to 1 or more subscriptions." , es );
153
+
154
+ if (reference .compareAndSet (subscriptions , UNSUBSCRIBED_SENTINEL )) {
155
+ unsubscribeAll (subscriptions );
156
+ break ;
134
157
}
135
158
} while (true );
136
159
}
0 commit comments