Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2929,6 +2929,39 @@ public void testSuccessfulAsList_partialFailure() throws Exception {
assertThat(results).containsExactly(null, DATA2).inOrder();
}

public void testSuccessfulAsList_withNullAndFailure() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = successfulAsList(future1, future2);

future1.set(null);
future2.setException(new Exception());

assertThat(getDone(compound)).containsExactly(null, null).inOrder();
}

public void testSuccessfulAsList_withCancellation() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = successfulAsList(future1, future2);

future1.cancel(false);
future2.set(DATA2);

assertThat(getDone(compound)).containsExactly(null, DATA2).inOrder();
}

public void testAllAsList_withNull() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = allAsList(future1, future2);

future1.set(null);
future2.set(DATA2);

assertThat(getDone(compound)).containsExactly(null, DATA2).inOrder();
}

public void testSuccessfulAsList_totalFailure() throws Exception {
SingleCallListener listener = new SingleCallListener();
SettableFuture<String> future1 = SettableFuture.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
package com.google.common.util.concurrent;

import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Lists;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.RetainedLocalRef;
import java.util.Collections;
import java.util.List;
import org.jspecify.annotations.Nullable;

Expand All @@ -36,18 +35,16 @@ abstract class CollectionFuture<V extends @Nullable Object, C extends @Nullable
* there: cancel() never reads this field, only writes to it. That makes the race here completely
* harmless, rather than just 99.99% harmless.
*/
@LazyInit private @Nullable List<@Nullable Present<V>> values;
@LazyInit private @Nullable List<@Nullable Object> values;

@SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types
CollectionFuture(
ImmutableCollection<? extends ListenableFuture<? extends V>> futures,
boolean allMustSucceed) {
super(futures, allMustSucceed, true);

List<@Nullable Present<V>> values =
futures.isEmpty()
? Collections.<@Nullable Present<V>>emptyList()
: Lists.<@Nullable Present<V>>newArrayListWithCapacity(futures.size());
List<@Nullable Object> values =
futures.isEmpty() ? emptyList() : newArrayListWithCapacity(futures.size());

// Populate the results list with null initially.
for (int i = 0; i < futures.size(); ++i) {
Expand All @@ -59,15 +56,15 @@ abstract class CollectionFuture<V extends @Nullable Object, C extends @Nullable

@Override
final void collectOneValue(int index, @ParametricNullness V returnValue) {
@RetainedLocalRef List<@Nullable Present<V>> localValues = values;
@RetainedLocalRef List<@Nullable Object> localValues = values;
if (localValues != null) {
localValues.set(index, new Present<>(returnValue));
localValues.set(index, returnValue == null ? NULL : returnValue);
}
}

@Override
final void handleAllCompleted() {
@RetainedLocalRef List<@Nullable Present<V>> localValues = values;
@RetainedLocalRef List<@Nullable Object> localValues = values;
if (localValues != null) {
set(combine(localValues));
}
Expand All @@ -79,7 +76,7 @@ void releaseResources(ReleaseResourcesReason reason) {
this.values = null;
}

abstract C combine(List<@Nullable Present<V>> values);
abstract C combine(List<@Nullable Object> values);

/** Used for {@link Futures#allAsList} and {@link Futures#successfulAsList}. */
static final class ListFuture<V extends @Nullable Object>
Expand All @@ -92,21 +89,16 @@ static final class ListFuture<V extends @Nullable Object>
}

@Override
public List<@Nullable V> combine(List<@Nullable Present<V>> values) {
public List<@Nullable V> combine(List<@Nullable Object> values) {
List<@Nullable V> result = newArrayListWithCapacity(values.size());
for (Present<V> element : values) {
result.add(element != null ? element.value : null);
for (Object element : values) {
@SuppressWarnings("unchecked") // we stored either V or NULL
V value = (element == null || element == NULL) ? null : (V) element;
result.add(value);
}
return unmodifiableList(result);
}
}

/** The result of a successful {@code Future}. */
private static final class Present<V extends @Nullable Object> {
@ParametricNullness final V value;

Present(@ParametricNullness V value) {
this.value = value;
}
}
private static final Object NULL = new Object();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2929,6 +2929,39 @@ public void testSuccessfulAsList_partialFailure() throws Exception {
assertThat(results).containsExactly(null, DATA2).inOrder();
}

public void testSuccessfulAsList_withNullAndFailure() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = successfulAsList(future1, future2);

future1.set(null);
future2.setException(new Exception());

assertThat(getDone(compound)).containsExactly(null, null).inOrder();
}

public void testSuccessfulAsList_withCancellation() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = successfulAsList(future1, future2);

future1.cancel(false);
future2.set(DATA2);

assertThat(getDone(compound)).containsExactly(null, DATA2).inOrder();
}

public void testAllAsList_withNull() throws Exception {
SettableFuture<String> future1 = SettableFuture.create();
SettableFuture<String> future2 = SettableFuture.create();
ListenableFuture<List<@Nullable String>> compound = allAsList(future1, future2);

future1.set(null);
future2.set(DATA2);

assertThat(getDone(compound)).containsExactly(null, DATA2).inOrder();
}

public void testSuccessfulAsList_totalFailure() throws Exception {
SingleCallListener listener = new SingleCallListener();
SettableFuture<String> future1 = SettableFuture.create();
Expand Down
36 changes: 14 additions & 22 deletions guava/src/com/google/common/util/concurrent/CollectionFuture.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
package com.google.common.util.concurrent;

import static com.google.common.collect.Lists.newArrayListWithCapacity;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;

import com.google.common.annotations.GwtCompatible;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Lists;
import com.google.errorprone.annotations.concurrent.LazyInit;
import com.google.j2objc.annotations.RetainedLocalRef;
import java.util.Collections;
import java.util.List;
import org.jspecify.annotations.Nullable;

Expand All @@ -36,18 +35,16 @@ abstract class CollectionFuture<V extends @Nullable Object, C extends @Nullable
* there: cancel() never reads this field, only writes to it. That makes the race here completely
* harmless, rather than just 99.99% harmless.
*/
@LazyInit private @Nullable List<@Nullable Present<V>> values;
@LazyInit private @Nullable List<@Nullable Object> values;

@SuppressWarnings("EmptyList") // ImmutableList doesn't support nullable element types
CollectionFuture(
ImmutableCollection<? extends ListenableFuture<? extends V>> futures,
boolean allMustSucceed) {
super(futures, allMustSucceed, true);

List<@Nullable Present<V>> values =
futures.isEmpty()
? Collections.<@Nullable Present<V>>emptyList()
: Lists.<@Nullable Present<V>>newArrayListWithCapacity(futures.size());
List<@Nullable Object> values =
futures.isEmpty() ? emptyList() : newArrayListWithCapacity(futures.size());

// Populate the results list with null initially.
for (int i = 0; i < futures.size(); ++i) {
Expand All @@ -59,15 +56,15 @@ abstract class CollectionFuture<V extends @Nullable Object, C extends @Nullable

@Override
final void collectOneValue(int index, @ParametricNullness V returnValue) {
@RetainedLocalRef List<@Nullable Present<V>> localValues = values;
@RetainedLocalRef List<@Nullable Object> localValues = values;
if (localValues != null) {
localValues.set(index, new Present<>(returnValue));
localValues.set(index, returnValue == null ? NULL : returnValue);
}
}

@Override
final void handleAllCompleted() {
@RetainedLocalRef List<@Nullable Present<V>> localValues = values;
@RetainedLocalRef List<@Nullable Object> localValues = values;
if (localValues != null) {
set(combine(localValues));
}
Expand All @@ -79,7 +76,7 @@ void releaseResources(ReleaseResourcesReason reason) {
this.values = null;
}

abstract C combine(List<@Nullable Present<V>> values);
abstract C combine(List<@Nullable Object> values);

/** Used for {@link Futures#allAsList} and {@link Futures#successfulAsList}. */
static final class ListFuture<V extends @Nullable Object>
Expand All @@ -92,21 +89,16 @@ static final class ListFuture<V extends @Nullable Object>
}

@Override
public List<@Nullable V> combine(List<@Nullable Present<V>> values) {
public List<@Nullable V> combine(List<@Nullable Object> values) {
List<@Nullable V> result = newArrayListWithCapacity(values.size());
for (Present<V> element : values) {
result.add(element != null ? element.value : null);
for (Object element : values) {
@SuppressWarnings("unchecked") // we stored either V or NULL
V value = (element == null || element == NULL) ? null : (V) element;
result.add(value);
}
return unmodifiableList(result);
}
}

/** The result of a successful {@code Future}. */
private static final class Present<V extends @Nullable Object> {
@ParametricNullness final V value;

Present(@ParametricNullness V value) {
this.value = value;
}
}
private static final Object NULL = new Object();
}
Loading