• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.inputmethod;
18 
19 import android.annotation.AnyThread;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 
23 import com.android.internal.annotations.GuardedBy;
24 
25 import java.util.ArrayList;
26 import java.util.concurrent.CompletableFuture;
27 
28 /**
29  * A utility class, which works as both a factory class of a cancellation signal to cancel
30  * all the completable objects.
31  *
32  * <p>TODO: Make this lock-free.</p>
33  */
34 public final class CancellationGroup {
35     private final Object mLock = new Object();
36 
37     /**
38      * List of {@link CompletableFuture}, which can be used to propagate {@link #cancelAll()} to
39      * completable objects.
40      *
41      * <p>This will be lazily instantiated to avoid unnecessary object allocations.</p>
42      */
43     @Nullable
44     @GuardedBy("mLock")
45     private ArrayList<CompletableFuture<?>> mFutureList = null;
46 
47     @GuardedBy("mLock")
48     private boolean mCanceled = false;
49 
50     /**
51      * Tries to register the given {@link CompletableFuture} into the callback list if this
52      * {@link CancellationGroup} is not yet cancelled.
53      *
54      * <p>If this {@link CancellationGroup} is already cancelled, then this method will immediately
55      * call {@link CompletableFuture#cancel(boolean)} then return {@code false}.</p>
56      *
57      * <p>When this method returns {@code true}, call {@link #unregisterFuture(CompletableFuture)}
58      * to remove the unnecessary object reference.</p>
59      *
60      * @param future {@link CompletableFuture} to be added to the cancellation callback list.
61      * @return {@code true} if the given {@code future} is added to the callback list.
62      *         {@code false} otherwise.
63      */
64     @AnyThread
tryRegisterFutureOrCancelImmediately(@onNull CompletableFuture<?> future)65     boolean tryRegisterFutureOrCancelImmediately(@NonNull CompletableFuture<?> future) {
66         synchronized (mLock) {
67             if (mCanceled) {
68                 future.cancel(false);
69                 return false;
70             }
71             if (mFutureList == null) {
72                 // Set the initial capacity to 1 with an assumption that usually there is up to 1
73                 // on-going operation.
74                 mFutureList = new ArrayList<>(1);
75             }
76             mFutureList.add(future);
77             return true;
78         }
79     }
80 
81     @AnyThread
unregisterFuture(@onNull CompletableFuture<?> future)82     void unregisterFuture(@NonNull CompletableFuture<?> future) {
83         synchronized (mLock) {
84             if (mFutureList != null) {
85                 mFutureList.remove(future);
86             }
87         }
88     }
89 
90     /**
91      * Cancel all the completable objects created from this {@link CancellationGroup}.
92      *
93      * <p>Secondary calls will be silently ignored.</p>
94      */
95     @AnyThread
cancelAll()96     public void cancelAll() {
97         synchronized (mLock) {
98             if (!mCanceled) {
99                 mCanceled = true;
100                 if (mFutureList != null) {
101                     mFutureList.forEach(future -> future.cancel(false));
102                     mFutureList.clear();
103                     mFutureList = null;
104                 }
105             }
106         }
107     }
108 
109     /**
110      * @return {@code true} if {@link #cancelAll()} is already called. {@code false} otherwise.
111      */
112     @AnyThread
isCanceled()113     public boolean isCanceled() {
114         synchronized (mLock) {
115             return mCanceled;
116         }
117     }
118 }
119