Skip to content

Commit 02e409b

Browse files
lfkelloggkaibolay
authored andcommitted
Remove synchronized usage in TaskCache (#4509)
* Remove synchronized usage in TaskCache * Re-arrange member declarations and make more consistent
1 parent f47ad31 commit 02e409b

File tree

2 files changed

+60
-32
lines changed

2 files changed

+60
-32
lines changed

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

Lines changed: 19 additions & 15 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,22 +66,23 @@ 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;
71+
@Lightweight private final Executor lightweightExecutor;
72+
@Blocking private final Executor blockingExecutor;
73+
private final SequentialReference<AppDistributionReleaseInternal> cachedNewRelease;
74+
private final TaskCache<AppDistributionRelease> checkForNewReleaseTaskCache;
75+
private final UpdateTaskCache updateIfNewReleaseAvailableTaskCache;
7276
private final FirebaseAppDistributionNotificationsManager notificationsManager;
73-
private TaskCache<UpdateTask> updateIfNewReleaseAvailableTaskCache = new TaskCache<>();
74-
private TaskCache<Task<AppDistributionRelease>> checkForNewReleaseTaskCache = new TaskCache<>();
75-
@Lightweight private Executor lightweightExecutor;
76-
private AlertDialog updateConfirmationDialog;
77-
private AlertDialog signInConfirmationDialog;
78-
@Nullable private Activity dialogHostActivity = null;
77+
private final AtomicBoolean feedbackInProgress = new AtomicBoolean(false);
78+
79+
@Nullable private AlertDialog updateConfirmationDialog;
80+
@Nullable private AlertDialog signInConfirmationDialog;
81+
@Nullable private Activity dialogHostActivity;
7982
private boolean remakeSignInConfirmationDialog = false;
8083
private boolean remakeUpdateConfirmationDialog = false;
81-
private TaskCompletionSource<Void> showSignInDialogTask = null;
82-
private TaskCompletionSource<Void> showUpdateDialogTask = null;
83-
private final AtomicBoolean feedbackInProgress = new AtomicBoolean(false);
84+
@Nullable private TaskCompletionSource<Void> showSignInDialogTask;
85+
@Nullable private TaskCompletionSource<Void> showUpdateDialogTask;
8486

8587
@VisibleForTesting
8688
FirebaseAppDistributionImpl(
@@ -94,7 +96,7 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
9496
@NonNull ReleaseIdentifier releaseIdentifier,
9597
@NonNull ScreenshotTaker screenshotTaker,
9698
@NonNull @Lightweight Executor lightweightExecutor,
97-
@NonNull Executor blockingExecutor) {
99+
@NonNull @Blocking Executor blockingExecutor) {
98100
this.firebaseApp = firebaseApp;
99101
this.testerSignInManager = testerSignInManager;
100102
this.newReleaseFetcher = newReleaseFetcher;
@@ -103,10 +105,12 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
103105
this.signInStorage = signInStorage;
104106
this.releaseIdentifier = releaseIdentifier;
105107
this.lifecycleNotifier = lifecycleNotifier;
106-
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor);
107-
this.lightweightExecutor = lightweightExecutor;
108108
this.screenshotTaker = screenshotTaker;
109+
this.lightweightExecutor = lightweightExecutor;
109110
this.blockingExecutor = blockingExecutor;
111+
this.cachedNewRelease = new SequentialReference<>(lightweightExecutor);
112+
this.checkForNewReleaseTaskCache = new TaskCache<>(lightweightExecutor);
113+
this.updateIfNewReleaseAvailableTaskCache = new UpdateTaskCache(lightweightExecutor);
110114
this.notificationsManager =
111115
new FirebaseAppDistributionNotificationsManager(firebaseApp.getApplicationContext());
112116
lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed);
@@ -117,7 +121,7 @@ class FirebaseAppDistributionImpl implements FirebaseAppDistribution {
117121
@Override
118122
@NonNull
119123
public UpdateTask updateIfNewReleaseAvailable() {
120-
return updateIfNewReleaseAvailableTaskCache.getOrCreateTask(
124+
return updateIfNewReleaseAvailableTaskCache.getOrCreateUpdateTask(
121125
() -> {
122126
UpdateTaskImpl updateTask = new UpdateTaskImpl();
123127
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)