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