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
17package com.android.documentsui;
18
19import android.os.AsyncTask;
20
21import com.android.internal.annotations.GuardedBy;
22import com.android.internal.util.Preconditions;
23import com.google.android.collect.Lists;
24import com.google.android.collect.Maps;
25
26import java.lang.ref.WeakReference;
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.concurrent.Executor;
30import java.util.concurrent.LinkedBlockingQueue;
31
32public class ProviderExecutor extends Thread implements Executor {
33
34    @GuardedBy("sExecutors")
35    private static HashMap<String, ProviderExecutor> sExecutors = Maps.newHashMap();
36
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 {
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
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     */
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
96    public void execute(Runnable command) {
97        preempt();
98        Preconditions.checkNotNull(command);
99        mQueue.add(command);
100    }
101
102    @Override
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