Skip to content

Commit 6c4c9f1

Browse files
Merge pull request #2759 from davidmoten/using-redev
Observable.using should use unsafeSubscribe and enable eager disposal
2 parents 94de8ee + ffe0fa1 commit 6c4c9f1

File tree

3 files changed

+338
-28
lines changed

3 files changed

+338
-28
lines changed

src/main/java/rx/Observable.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2547,7 +2547,7 @@ public final static Observable<Long> timer(long delay, TimeUnit unit, Scheduler
25472547
}
25482548

25492549
/**
2550-
* Constructs an Observable that creates a dependent resource object.
2550+
* Constructs an Observable that creates a dependent resource object which is disposed of on unsubscription.
25512551
* <p>
25522552
* <img width="640" height="400" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/using.png" alt="">
25532553
* <dl>
@@ -2568,7 +2568,42 @@ public final static <T, Resource> Observable<T> using(
25682568
final Func0<Resource> resourceFactory,
25692569
final Func1<? super Resource, ? extends Observable<? extends T>> observableFactory,
25702570
final Action1<? super Resource> disposeAction) {
2571-
return create(new OnSubscribeUsing<T, Resource>(resourceFactory, observableFactory, disposeAction));
2571+
return using(resourceFactory, observableFactory, disposeAction, false);
2572+
}
2573+
2574+
/**
2575+
* Constructs an Observable that creates a dependent resource object which is disposed of just before
2576+
* termination if <code>disposeEagerly</code> is set to true and unsubscription does not occur before termination. Otherwise
2577+
* resource disposal will occur on unsubscription. Eager disposal is particularly appropriate for a synchronous observable
2578+
* that resuses resources. <code>disposeAction</code> will only be called once per subscription.
2579+
* <p>
2580+
* <img width="640" height="400" src="https://raw.github.com/wiki/ReactiveX/RxJava/images/rx-operators/using.png" alt="">
2581+
* <dl>
2582+
* <dt><b>Scheduler:</b></dt>
2583+
* <dd>{@code using} does not operate by default on a particular {@link Scheduler}.</dd>
2584+
* </dl>
2585+
*
2586+
* @param resourceFactory
2587+
* the factory function to create a resource object that depends on the Observable
2588+
* @param observableFactory
2589+
* the factory function to create an Observable
2590+
* @param disposeAction
2591+
* the function that will dispose of the resource
2592+
* @param disposeEagerly
2593+
* if true then disposal will happen either on unsubscription or just before emission of
2594+
* a terminal event (onComplete or onError).
2595+
* @return the Observable whose lifetime controls the lifetime of the dependent resource object
2596+
* @see <a href="http://reactivex.io/documentation/operators/using.html">ReactiveX operators documentation: Using</a>
2597+
* @Experimental The behavior of this can change at any time.
2598+
* @since (if this graduates from Experimental/Beta to supported, replace
2599+
* this parenthetical with the release number)
2600+
*/
2601+
@Experimental
2602+
public final static <T, Resource> Observable<T> using(
2603+
final Func0<Resource> resourceFactory,
2604+
final Func1<? super Resource, ? extends Observable<? extends T>> observableFactory,
2605+
final Action1<? super Resource> disposeAction, boolean disposeEagerly) {
2606+
return create(new OnSubscribeUsing<T, Resource>(resourceFactory, observableFactory, disposeAction, disposeEagerly));
25722607
}
25732608

25742609
/**

src/main/java/rx/internal/operators/OnSubscribeUsing.java

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
*/
1616
package rx.internal.operators;
1717

18+
import java.util.Arrays;
19+
import java.util.concurrent.atomic.AtomicBoolean;
20+
1821
import rx.Observable;
1922
import rx.Observable.OnSubscribe;
2023
import rx.Subscriber;
24+
import rx.Subscription;
25+
import rx.exceptions.CompositeException;
2126
import rx.functions.Action0;
2227
import rx.functions.Action1;
2328
import rx.functions.Func0;
2429
import rx.functions.Func1;
25-
import rx.subscriptions.Subscriptions;
2630

2731
/**
2832
* Constructs an observable sequence that depends on a resource object.
@@ -32,35 +36,103 @@ public final class OnSubscribeUsing<T, Resource> implements OnSubscribe<T> {
3236
private final Func0<Resource> resourceFactory;
3337
private final Func1<? super Resource, ? extends Observable<? extends T>> observableFactory;
3438
private final Action1<? super Resource> dispose;
39+
private final boolean disposeEagerly;
3540

3641
public OnSubscribeUsing(Func0<Resource> resourceFactory,
3742
Func1<? super Resource, ? extends Observable<? extends T>> observableFactory,
38-
Action1<? super Resource> dispose) {
43+
Action1<? super Resource> dispose, boolean disposeEagerly) {
3944
this.resourceFactory = resourceFactory;
4045
this.observableFactory = observableFactory;
4146
this.dispose = dispose;
47+
this.disposeEagerly = disposeEagerly;
4248
}
4349

4450
@Override
4551
public void call(Subscriber<? super T> subscriber) {
46-
try {
47-
final Resource resource = resourceFactory.call();
48-
subscriber.add(Subscriptions.create(new Action0() {
4952

50-
@Override
51-
public void call() {
52-
dispose.call(resource);
53-
}
53+
try {
5454

55-
}));
56-
Observable<? extends T> observable = observableFactory.call(resource);
57-
observable.subscribe(subscriber);
55+
// create the resource
56+
final Resource resource = resourceFactory.call();
57+
// create an action/subscription that disposes only once
58+
final DisposeAction<Resource> disposeOnceOnly = new DisposeAction<Resource>(dispose,
59+
resource);
60+
// dispose on unsubscription
61+
subscriber.add(disposeOnceOnly);
62+
// create the observable
63+
final Observable<? extends T> source = observableFactory
64+
// create the observable
65+
.call(resource);
66+
final Observable<? extends T> observable;
67+
// supplement with on termination disposal if requested
68+
if (disposeEagerly)
69+
observable = source
70+
// dispose on completion or error
71+
.doOnTerminate(disposeOnceOnly);
72+
else
73+
observable = source;
74+
try {
75+
// start
76+
observable.unsafeSubscribe(subscriber);
77+
} catch (Throwable e) {
78+
Throwable disposeError = disposeEagerlyIfRequested(disposeOnceOnly);
79+
if (disposeError != null)
80+
subscriber.onError(new CompositeException(Arrays.asList(e, disposeError)));
81+
else
82+
// propagate error
83+
subscriber.onError(e);
84+
}
5885
} catch (Throwable e) {
59-
// eagerly call unsubscribe since this operator is specifically about resource management
60-
subscriber.unsubscribe();
6186
// then propagate error
6287
subscriber.onError(e);
6388
}
6489
}
6590

91+
private Throwable disposeEagerlyIfRequested(final Action0 disposeOnceOnly) {
92+
if (disposeEagerly)
93+
try {
94+
disposeOnceOnly.call();
95+
return null;
96+
} catch (Throwable e) {
97+
return e;
98+
}
99+
else
100+
return null;
101+
}
102+
103+
private static final class DisposeAction<Resource> extends AtomicBoolean implements Action0,
104+
Subscription {
105+
private static final long serialVersionUID = 4262875056400218316L;
106+
107+
private Action1<? super Resource> dispose;
108+
private Resource resource;
109+
110+
private DisposeAction(Action1<? super Resource> dispose, Resource resource) {
111+
this.dispose = dispose;
112+
this.resource = resource;
113+
lazySet(false); // StoreStore barrier
114+
}
115+
116+
@Override
117+
public void call() {
118+
if (compareAndSet(false, true)) {
119+
try {
120+
dispose.call(resource);
121+
} finally {
122+
resource = null;
123+
dispose = null;
124+
}
125+
}
126+
}
127+
128+
@Override
129+
public boolean isUnsubscribed() {
130+
return get();
131+
}
132+
133+
@Override
134+
public void unsubscribe() {
135+
call();
136+
}
137+
}
66138
}

0 commit comments

Comments
 (0)