Skip to content

Commit 5979f78

Browse files
committed
Remove synchronized usage in TaskCache
1 parent 98e29b9 commit 5979f78

File tree

2 files changed

+53
-26
lines changed

2 files changed

+53
-26
lines changed

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

Lines changed: 12 additions & 9 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;
@@ -65,14 +66,14 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
6566
private final ApkUpdater apkUpdater;
6667
private final AabUpdater aabUpdater;
6768
private final SignInStorage signInStorage;
68-
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease;
6969
private final ReleaseIdentifier releaseIdentifier;
7070
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<>();
7571
@Lightweight private Executor lightweightExecutor;
72+
@Blocking private final Executor blockingExecutor;
73+
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease;
74+
private TaskCache<AppDistributionRelease> checkForNewReleaseTaskCache;
75+
private UpdateTaskCache updateIfNewReleaseAvailableTaskCache;
76+
private final FirebaseAppDistributionNotificationsManager notificationsManager;
7677
private AlertDialog updateConfirmationDialog;
7778
private AlertDialog signInConfirmationDialog;
7879
@Nullable private Activity dialogHostActivity = null;
@@ -94,7 +95,7 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
9495
@NonNull ReleaseIdentifier releaseIdentifier,
9596
@NonNull ScreenshotTaker screenshotTaker,
9697
@NonNull @Lightweight Executor lightweightExecutor,
97-
@NonNull Executor blockingExecutor) {
98+
@NonNull @Blocking Executor blockingExecutor) {
9899
this.firebaseApp = firebaseApp;
99100
this.testerSignInManager = testerSignInManager;
100101
this.newReleaseFetcher = newReleaseFetcher;
@@ -103,10 +104,12 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
103104
this.signInStorage = signInStorage;
104105
this.releaseIdentifier = releaseIdentifier;
105106
this.lifecycleNotifier = lifecycleNotifier;
106-
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor);
107-
this.lightweightExecutor = lightweightExecutor;
108107
this.screenshotTaker = screenshotTaker;
108+
this.lightweightExecutor = lightweightExecutor;
109109
this.blockingExecutor = blockingExecutor;
110+
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor);
111+
this.checkForNewReleaseTaskCache = new TaskCache<>(lightweightExecutor);
112+
this.updateIfNewReleaseAvailableTaskCache = new UpdateTaskCache(lightweightExecutor);
110113
this.notificationsManager =
111114
new FirebaseAppDistributionNotificationsManager(firebaseApp.getApplicationContext());
112115
lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed);
@@ -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
}

0 commit comments

Comments
 (0)