1 /*
2  * Copyright (C) 2013 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.documentsui;
18 
19 import android.os.AsyncTask;
20 
21 import androidx.annotation.GuardedBy;
22 
23 import java.lang.ref.WeakReference;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.concurrent.Executor;
27 import java.util.concurrent.LinkedBlockingQueue;
28 
29 public class ProviderExecutor extends Thread implements Executor {
30 
31     @GuardedBy("sExecutors")
32     private static HashMap<String, ProviderExecutor> sExecutors = new HashMap<>();
33 
forAuthority(String authority)34     public static ProviderExecutor forAuthority(String authority) {
35         synchronized (sExecutors) {
36             ProviderExecutor executor = sExecutors.get(authority);
37             if (executor == null) {
38                 executor = new ProviderExecutor();
39                 executor.setName("ProviderExecutor: " + authority);
40                 executor.start();
41                 sExecutors.put(authority, executor);
42             }
43             return executor;
44         }
45     }
46 
47     public interface Preemptable {
preempt()48         void preempt();
49     }
50 
51     private final LinkedBlockingQueue<Runnable> mQueue = new LinkedBlockingQueue<Runnable>();
52 
53     private final ArrayList<WeakReference<Preemptable>> mPreemptable = new ArrayList<>();
54 
preempt()55     private void preempt() {
56         synchronized (mPreemptable) {
57             int count = 0;
58             for (WeakReference<Preemptable> ref : mPreemptable) {
59                 final Preemptable p = ref.get();
60                 if (p != null) {
61                     count++;
62                     p.preempt();
63                 }
64             }
65             mPreemptable.clear();
66         }
67     }
68 
69     /**
70      * Execute the given task. If given task is not {@link Preemptable}, it will
71      * preempt all outstanding preemptable tasks.
72      */
execute(AsyncTask<P, ?, ?> task, P... params)73     public <P> void execute(AsyncTask<P, ?, ?> task, P... params) {
74         if (task instanceof Preemptable) {
75             synchronized (mPreemptable) {
76                 mPreemptable.add(new WeakReference<Preemptable>((Preemptable) task));
77             }
78             task.executeOnExecutor(mNonPreemptingExecutor, params);
79         } else {
80             task.executeOnExecutor(this, params);
81         }
82     }
83 
84     private Executor mNonPreemptingExecutor = new Executor() {
85         @Override
86         public void execute(Runnable command) {
87             assert(command != null);
88             mQueue.add(command);
89         }
90     };
91 
92     @Override
execute(Runnable command)93     public void execute(Runnable command) {
94         preempt();
95         assert(command != null);
96         mQueue.add(command);
97     }
98 
99     @Override
run()100     public void run() {
101         while (true) {
102             try {
103                 final Runnable command = mQueue.take();
104                 command.run();
105             } catch (InterruptedException e) {
106                 // That was weird; let's go look for more tasks.
107             }
108         }
109     }
110 }
111