Skip to content

Commit

Permalink
refreshable.map calls the map function exactly once (#632)
Browse files Browse the repository at this point in the history
refreshable.map calls the map function exactly once
  • Loading branch information
carterkozak committed May 6, 2024
1 parent 38bd837 commit a2e711e
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 4 deletions.
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-632.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: refreshable.map calls the map function exactly once
links:
- https://github.com/palantir/refreshable/pull/632
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public Disposable subscribe(Consumer<? super T> throwingSubscriber) {
SideEffectSubscriber<? super T> trackedSubscriber =
rootSubscriberTracker.newSideEffectSubscriber(throwingSubscriber, this);

Disposable disposable = subscribeToSelf(trackedSubscriber);
Disposable disposable = subscribeToSelf(trackedSubscriber, true);
return new SubscribeDisposable(disposable, rootSubscriberTracker, trackedSubscriber);
} finally {
readLock.unlock();
Expand Down Expand Up @@ -161,10 +161,12 @@ public void dispose() {
}

@GuardedBy("readLock")
private Disposable subscribeToSelf(Consumer<? super T> subscriber) {
private Disposable subscribeToSelf(Consumer<? super T> subscriber, boolean updateSubscriber) {
preSubscribeLogging();
orderedSubscribers.add(subscriber);
subscriber.accept(current);
if (updateSubscriber) {
subscriber.accept(current);
}
return new DefaultDisposable(orderedSubscribers, subscriber);
}

Expand Down Expand Up @@ -210,7 +212,9 @@ public <R> Refreshable<R> map(Function<? super T, R> function) {
DefaultRefreshable<R> child = createChild(initialChildValue);

MapSubscriber<? super T, R> mapSubscriber = new MapSubscriber<>(function, child);
Disposable cleanUp = subscribeToSelf(mapSubscriber);
// Do not update the subscriber here because we've just computed the value while
// holding readLock above to ensure bad functions throw on 'map' invocation.
Disposable cleanUp = subscribeToSelf(mapSubscriber, false);
REFRESHABLE_CLEANER.register(child, cleanUp::dispose);
return child;
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ public void testMap_noEventsWhenDerivedValueUnchanged() throws Exception {
verifyNoMoreInteractions(consumer, derivedConsumer);
}

@Test
public void testMap_calledOnce() {
SettableRefreshable<AtomicInteger> ref = Refreshable.create(new AtomicInteger());
assertThat(ref.get()).hasValue(0);
Refreshable<Integer> mapped = ref.map(AtomicInteger::incrementAndGet);
assertThat(mapped.get()).isEqualTo(1);
assertThat(ref.get()).hasValue(1);
}

@Test
public void testMap_throws() {
SettableRefreshable<String> ref = Refreshable.create("");
RuntimeException thrown = new RuntimeException();
assertThatThrownBy(() -> ref.map(_ignored -> {
throw thrown;
}))
.isSameAs(thrown);
}

@Test
public void testSubscribe() throws Exception {
refreshable.subscribe(consumer);
Expand Down

0 comments on commit a2e711e

Please sign in to comment.