|
15 | 15 | package com.google.firebase.appdistribution.impl;
|
16 | 16 |
|
17 | 17 | 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; |
18 | 22 |
|
19 | 23 | /**
|
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. |
22 | 29 | */
|
23 |
| -class TaskCache<T extends Task> { |
| 30 | +class TaskCache<T> { |
24 | 31 |
|
25 |
| - /** A functional interface for a producer of a new Task. */ |
| 32 | + /** A functional interface for a producer of a new {@link Task}. */ |
26 | 33 | @FunctionalInterface
|
27 |
| - interface TaskProducer<T extends Task> { |
| 34 | + interface TaskProducer<T> { |
28 | 35 |
|
29 |
| - /** Produce a new Task. */ |
30 |
| - T produce(); |
| 36 | + /** Produce a new {@link Task}. */ |
| 37 | + Task<T> produce(); |
31 | 38 | }
|
32 | 39 |
|
33 |
| - private T cachedTask; |
| 40 | + private Task<T> cachedTask; |
| 41 | + private final Executor sequentialExecutor; |
34 | 42 |
|
35 | 43 | /**
|
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. |
38 | 46 | *
|
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. |
41 | 48 | */
|
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(); |
47 | 71 | }
|
48 | 72 | }
|
0 commit comments