Skip to content

Commit 20c9c91

Browse files
committed
1.x: fix using() resource cleanup when factory throws or being non-eager
1 parent 1512c10 commit 20c9c91

File tree

2 files changed

+116
-37
lines changed

2 files changed

+116
-37
lines changed

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

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

18-
import java.util.Arrays;
1918
import java.util.concurrent.atomic.AtomicBoolean;
2019

2120
import rx.*;
@@ -26,6 +25,9 @@
2625

2726
/**
2827
* Constructs an observable sequence that depends on a resource object.
28+
*
29+
* @param <T> the output value type
30+
* @param <Resource> the resource type
2931
*/
3032
public final class OnSubscribeUsing<T, Resource> implements OnSubscribe<T> {
3133

@@ -56,26 +58,46 @@ public void call(final Subscriber<? super T> subscriber) {
5658
// dispose on unsubscription
5759
subscriber.add(disposeOnceOnly);
5860
// create the observable
59-
final Observable<? extends T> source = observableFactory
60-
// create the observable
61-
.call(resource);
61+
final Observable<? extends T> source;
62+
63+
try {
64+
source = observableFactory
65+
// create the observable
66+
.call(resource);
67+
} catch (Throwable e) {
68+
Throwable disposeError = dispose(disposeOnceOnly);
69+
Exceptions.throwIfFatal(e);
70+
Exceptions.throwIfFatal(disposeError);
71+
if (disposeError != null) {
72+
subscriber.onError(new CompositeException(e, disposeError));
73+
} else {
74+
// propagate error
75+
subscriber.onError(e);
76+
}
77+
return;
78+
}
79+
6280
final Observable<? extends T> observable;
6381
// supplement with on termination disposal if requested
64-
if (disposeEagerly)
82+
if (disposeEagerly) {
6583
observable = source
6684
// dispose on completion or error
6785
.doOnTerminate(disposeOnceOnly);
68-
else
69-
observable = source;
86+
} else {
87+
observable = source
88+
// dispose after the terminal signals were sent out
89+
.doAfterTerminate(disposeOnceOnly);
90+
}
91+
7092
try {
7193
// start
7294
observable.unsafeSubscribe(Subscribers.wrap(subscriber));
7395
} catch (Throwable e) {
74-
Throwable disposeError = disposeEagerlyIfRequested(disposeOnceOnly);
96+
Throwable disposeError = dispose(disposeOnceOnly);
7597
Exceptions.throwIfFatal(e);
7698
Exceptions.throwIfFatal(disposeError);
7799
if (disposeError != null)
78-
subscriber.onError(new CompositeException(Arrays.asList(e, disposeError)));
100+
subscriber.onError(new CompositeException(e, disposeError));
79101
else
80102
// propagate error
81103
subscriber.onError(e);
@@ -86,16 +108,13 @@ public void call(final Subscriber<? super T> subscriber) {
86108
}
87109
}
88110

89-
private Throwable disposeEagerlyIfRequested(final Action0 disposeOnceOnly) {
90-
if (disposeEagerly)
91-
try {
92-
disposeOnceOnly.call();
93-
return null;
94-
} catch (Throwable e) {
95-
return e;
96-
}
97-
else
111+
private Throwable dispose(final Action0 disposeOnceOnly) {
112+
try {
113+
disposeOnceOnly.call();
98114
return null;
115+
} catch (Throwable e) {
116+
return e;
117+
}
99118
}
100119

101120
private static final class DisposeAction<Resource> extends AtomicBoolean implements Action0,

src/test/java/rx/internal/operators/OnSubscribeUsingTest.java

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,22 @@
1515
*/
1616
package rx.internal.operators;
1717

18-
import static org.junit.Assert.assertEquals;
19-
import static org.junit.Assert.fail;
20-
import static org.mockito.Mockito.inOrder;
21-
import static org.mockito.Mockito.mock;
22-
import static org.mockito.Mockito.times;
23-
import static org.mockito.Mockito.verify;
24-
import static org.mockito.Mockito.when;
25-
26-
import java.util.ArrayList;
27-
import java.util.Arrays;
28-
import java.util.List;
29-
30-
import org.junit.Test;
18+
import static org.junit.Assert.*;
19+
import static org.mockito.Mockito.*;
20+
21+
import java.util.*;
22+
import java.util.concurrent.atomic.AtomicInteger;
23+
24+
import org.junit.*;
3125
import org.mockito.InOrder;
3226

27+
import rx.*;
3328
import rx.Observable;
3429
import rx.Observable.OnSubscribe;
3530
import rx.Observer;
36-
import rx.Subscriber;
37-
import rx.Subscription;
3831
import rx.exceptions.TestException;
39-
import rx.functions.Action0;
40-
import rx.functions.Action1;
41-
import rx.functions.Func0;
42-
import rx.functions.Func1;
32+
import rx.functions.*;
33+
import rx.observers.TestSubscriber;
4334
import rx.subscriptions.Subscriptions;
4435

4536
public class OnSubscribeUsingTest {
@@ -432,4 +423,73 @@ public void call() {
432423
};
433424
}
434425

426+
@Test
427+
public void factoryThrows() {
428+
429+
TestSubscriber<Integer> ts = TestSubscriber.create();
430+
431+
final AtomicInteger count = new AtomicInteger();
432+
433+
Observable.<Integer, Integer>using(
434+
new Func0<Integer>() {
435+
@Override
436+
public Integer call() {
437+
return 1;
438+
}
439+
},
440+
new Func1<Integer, Observable<Integer>>() {
441+
@Override
442+
public Observable<Integer> call(Integer v) {
443+
throw new TestException("forced failure");
444+
}
445+
},
446+
new Action1<Integer>() {
447+
@Override
448+
public void call(Integer c) {
449+
count.incrementAndGet();
450+
}
451+
}
452+
)
453+
.unsafeSubscribe(ts);
454+
455+
ts.assertError(TestException.class);
456+
457+
Assert.assertEquals(1, count.get());
458+
}
459+
460+
@Test
461+
public void nonEagerTermination() {
462+
463+
TestSubscriber<Integer> ts = TestSubscriber.create();
464+
465+
final AtomicInteger count = new AtomicInteger();
466+
467+
Observable.<Integer, Integer>using(
468+
new Func0<Integer>() {
469+
@Override
470+
public Integer call() {
471+
return 1;
472+
}
473+
},
474+
new Func1<Integer, Observable<Integer>>() {
475+
@Override
476+
public Observable<Integer> call(Integer v) {
477+
return Observable.just(v);
478+
}
479+
},
480+
new Action1<Integer>() {
481+
@Override
482+
public void call(Integer c) {
483+
count.incrementAndGet();
484+
}
485+
}, false
486+
)
487+
.unsafeSubscribe(ts);
488+
489+
ts.assertValue(1);
490+
ts.assertNoErrors();
491+
ts.assertCompleted();
492+
493+
Assert.assertEquals(1, count.get());
494+
}
435495
}

0 commit comments

Comments
 (0)