Skip to content

Commit f9c79ae

Browse files
committed
Remove synchronized usage in TaskCache
1 parent 1d7428c commit f9c79ae

File tree

3 files changed

+50
-24
lines changed

3 files changed

+50
-24
lines changed

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/FirebaseAppDistributionImpl.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.google.android.gms.tasks.TaskCompletionSource;
3737
import com.google.android.gms.tasks.Tasks;
3838
import com.google.firebase.FirebaseApp;
39+
import com.google.firebase.annotations.concurrent.Blocking;
3940
import com.google.firebase.annotations.concurrent.Lightweight;
4041
import com.google.firebase.appdistribution.AppDistributionRelease;
4142
import com.google.firebase.appdistribution.BinaryType;
@@ -68,11 +69,11 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
6869
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease;
6970
private final ReleaseIdentifier releaseIdentifier;
7071
private final ScreenshotTaker screenshotTaker;
71-
private final Executor blockingExecutor;
72-
private final FirebaseAppDistributionNotificationsManager notificationsManager;
73-
private TaskCache<UpdateTask> updateIfNewReleaseAvailableTaskCache = new TaskCache<>();
74-
private TaskCache<Task<AppDistributionRelease>> checkForNewReleaseTaskCache = new TaskCache<>();
72+
@Blocking private final Executor blockingExecutor;
7573
@Lightweight private Executor lightweightExecutor;
74+
private final FirebaseAppDistributionNotificationsManager notificationsManager;
75+
private UpdateTaskCache updateIfNewReleaseAvailableTaskCache;
76+
private TaskCache<AppDistributionRelease> checkForNewReleaseTaskCache;
7677
private AlertDialog updateConfirmationDialog;
7778
private AlertDialog signInConfirmationDialog;
7879
@Nullable private Activity dialogHostActivity = null;
@@ -109,6 +110,8 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
109110
this.blockingExecutor = blockingExecutor;
110111
this.notificationsManager =
111112
new FirebaseAppDistributionNotificationsManager(firebaseApp.getApplicationContext());
113+
this.updateIfNewReleaseAvailableTaskCache = new UpdateTaskCache(lightweightExecutor);
114+
this.checkForNewReleaseTaskCache = new TaskCache<>(lightweightExecutor);
112115
lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed);
113116
lifecycleNotifier.addOnActivityPausedListener(this::onActivityPaused);
114117
lifecycleNotifier.addOnActivityResumedListener(this::onActivityResumed);
@@ -117,7 +120,7 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
117120
@Override
118121
@NonNull
119122
public UpdateTask updateIfNewReleaseAvailable() {
120-
return updateIfNewReleaseAvailableTaskCache.getOrCreateTask(
123+
return updateIfNewReleaseAvailableTaskCache.getOrCreateUpdateTask(
121124
() -> {
122125
UpdateTaskImpl updateTask = new UpdateTaskImpl();
123126
remakeSignInConfirmationDialog = false;

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/impl/TaskCache.java

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,34 +15,58 @@
1515
package com.google.firebase.appdistribution.impl;
1616

1717
import com.google.android.gms.tasks.Task;
18+
import com.google.android.gms.tasks.TaskCompletionSource;
19+
import com.google.firebase.annotations.concurrent.Lightweight;
20+
import com.google.firebase.concurrent.FirebaseExecutors;
21+
import java.util.concurrent.Executor;
1822

1923
/**
20-
* A cache for Tasks, for use in cases where we only ever want one active task at a time for a
21-
* particular operation.
24+
* A cache for a {@link Task}, for use in cases where we only ever want one active task at a time
25+
* for a particular operation.
26+
*
27+
* <p>If you need a reference to an underlying TaskCompletionSource, use {@link
28+
* TaskCompletionSourceCache} instead.
2229
*/
23-
class TaskCache<T extends Task> {
30+
class TaskCache<T> {
2431

25-
/** A functional interface for a producer of a new Task. */
32+
/** A functional interface for a producer of a new {@link Task}. */
2633
@FunctionalInterface
27-
interface TaskProducer<T extends Task> {
34+
interface TaskProducer<T> {
2835

29-
/** Produce a new Task. */
30-
T produce();
36+
/** Produce a new {@link Task}. */
37+
Task<T> produce();
3138
}
3239

33-
private T cachedTask;
40+
private Task<T> cachedTask;
41+
private final Executor sequentialExecutor;
3442

3543
/**
36-
* Gets a cached task, if there is one and it is not completed, or else calls the given {@code
37-
* producer} and caches the returned task.
44+
* Constructor for a {@link TaskCache} that controls access using its own sequential executor
45+
* backed by the given base executor.
3846
*
39-
* @return the cached task if there is one and it is not completed, or else the result from {@code
40-
* producer.produce()}
47+
* @param baseExecutor Executor (typically {@link Lightweight}) to back the sequential executor.
4148
*/
42-
synchronized T getOrCreateTask(TaskProducer<T> producer) {
43-
if (cachedTask == null || cachedTask.isComplete()) {
44-
cachedTask = producer.produce();
45-
}
46-
return cachedTask;
49+
TaskCache(Executor baseExecutor) {
50+
sequentialExecutor = FirebaseExecutors.newSequentialExecutor(baseExecutor);
51+
}
52+
53+
/**
54+
* Gets a cached {@link Task}, if there is one and it is not completed, or else calls the given
55+
* {@code producer} and caches the return value.
56+
*/
57+
Task<T> getOrCreateTask(TaskProducer<T> producer) {
58+
TaskCompletionSource<T> taskCompletionSource = new TaskCompletionSource<>();
59+
sequentialExecutor.execute(
60+
() -> {
61+
if (!isOngoing(cachedTask)) {
62+
cachedTask = producer.produce();
63+
}
64+
TaskUtils.shadowTask(taskCompletionSource, cachedTask);
65+
});
66+
return taskCompletionSource.getTask();
67+
}
68+
69+
private static <T> boolean isOngoing(Task<T> task) {
70+
return task != null && !task.isComplete();
4771
}
4872
}

firebase-appdistribution/src/test/java/com/google/firebase/appdistribution/impl/ApkUpdaterTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,7 @@ public void updateApk_whenSuccessfullyUpdated_notificationsSetCorrectly()
196196
.thenAnswer(answerWithValueAfterSleep(Tasks.forResult(null)));
197197

198198
UpdateTask updateTask = apkUpdater.updateApk(TEST_RELEASE, true);
199-
List<UpdateProgress> events =
200-
awaitProgressEvents(updateTask, 3);
199+
List<UpdateProgress> events = awaitProgressEvents(updateTask, 3);
201200

202201
assertThat(events).hasSize(3);
203202
assertThat(events.get(0).getUpdateStatus()).isEqualTo(UpdateStatus.PENDING);

0 commit comments

Comments
 (0)