1113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin/*
2113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * Copyright (C) 2011 The Android Open Source Project
3113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin *
4113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * you may not use this file except in compliance with the License.
6113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * You may obtain a copy of the License at
7113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin *
8113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin *
10113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * Unless required by applicable law or agreed to in writing, software
11113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * See the License for the specific language governing permissions and
14113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin * limitations under the License.
15113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin */
16113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
17113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linpackage com.android.gallery3d.util;
18113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
19113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport com.android.gallery3d.common.Utils;
20113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport com.android.gallery3d.util.ThreadPool.Job;
21113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport com.android.gallery3d.util.ThreadPool.JobContext;
22113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
23113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport java.util.LinkedList;
24113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
25113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin// Limit the number of concurrent jobs that has been submitted into a ThreadPool
26113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin@SuppressWarnings("rawtypes")
27113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linpublic class JobLimiter implements FutureListener {
28113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static final String TAG = "JobLimiter";
29113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
30113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    // State Transition:
31113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    //      INIT -> DONE, CANCELLED
32113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    //      DONE -> CANCELLED
33113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static final int STATE_INIT = 0;
34113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static final int STATE_DONE = 1;
35113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static final int STATE_CANCELLED = 2;
36113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
37113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private final LinkedList<JobWrapper<?>> mJobs = new LinkedList<JobWrapper<?>>();
38113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private final ThreadPool mPool;
39113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private int mLimit;
40113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
41113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static class JobWrapper<T> implements Future<T>, Job<T> {
42113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        private int mState = STATE_INIT;
43113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        private Job<T> mJob;
44113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        private Future<T> mDelegate;
45113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        private FutureListener<T> mListener;
46113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        private T mResult;
47113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
48113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public JobWrapper(Job<T> job, FutureListener<T> listener) {
49113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            mJob = job;
50113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            mListener = listener;
51113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
52113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
53113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public synchronized void setFuture(Future<T> future) {
54113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            if (mState != STATE_INIT) return;
55113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            mDelegate = future;
56113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
57113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
58113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
59113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public void cancel() {
60113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            FutureListener<T> listener = null;
61113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            synchronized (this) {
62113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                if (mState != STATE_DONE) {
63113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    listener = mListener;
64113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    mJob = null;
65113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    mListener = null;
66113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    if (mDelegate != null) {
67113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                        mDelegate.cancel();
68113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                        mDelegate = null;
69113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                    }
70113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                }
71113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mState = STATE_CANCELLED;
72113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mResult = null;
73113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                notifyAll();
74113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
75113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            if (listener != null) listener.onFutureDone(this);
76113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
77113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
78113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
79113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public synchronized boolean isCancelled() {
80113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            return mState == STATE_CANCELLED;
81113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
82113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
83113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
84113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public boolean isDone() {
85113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            // Both CANCELLED AND DONE is considered as done
86113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            return mState !=  STATE_INIT;
87113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
88113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
89113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
90113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public synchronized T get() {
91113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            while (mState == STATE_INIT) {
92113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                // handle the interrupted exception of wait()
93113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                Utils.waitWithoutInterrupt(this);
94113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
95113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            return mResult;
96113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
97113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
98113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
99113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public void waitDone() {
100113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            get();
101113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
102113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
103113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        @Override
104113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        public T run(JobContext jc) {
105113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            Job<T> job = null;
106113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            synchronized (this) {
107113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                if (mState == STATE_CANCELLED) return null;
108113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                job = mJob;
109113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
110113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            T result  = null;
111113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            try {
112113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                result = job.run(jc);
113113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            } catch (Throwable t) {
114113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                Log.w(TAG, "error executing job: " + job, t);
115113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
116113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            FutureListener<T> listener = null;
117113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            synchronized (this) {
118113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                if (mState == STATE_CANCELLED) return null;
119113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mState = STATE_DONE;
120113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                listener = mListener;
121113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mListener = null;
122113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mJob = null;
123113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                mResult = result;
124113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                notifyAll();
125113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
126113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            if (listener != null) listener.onFutureDone(this);
127113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            return result;
128113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
129113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    }
130113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
131113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    public JobLimiter(ThreadPool pool, int limit) {
132113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        mPool = Utils.checkNotNull(pool);
133113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        mLimit = limit;
134113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    }
135113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
136113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    public synchronized <T> Future<T> submit(Job<T> job, FutureListener<T> listener) {
137113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        JobWrapper<T> future = new JobWrapper<T>(Utils.checkNotNull(job), listener);
138113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        mJobs.addLast(future);
139113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        submitTasksIfAllowed();
140113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        return future;
141113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    }
142113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
143113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    @SuppressWarnings({"rawtypes", "unchecked"})
144113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private void submitTasksIfAllowed() {
145113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        while (mLimit > 0 && !mJobs.isEmpty()) {
146113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            JobWrapper wrapper = mJobs.removeFirst();
147113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            if (!wrapper.isCancelled()) {
148113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                --mLimit;
149113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin                wrapper.setFuture(mPool.submit(wrapper, this));
150113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            }
151113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        }
152113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    }
153113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin
154113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    @Override
155113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    public synchronized void onFutureDone(Future future) {
156113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        ++mLimit;
157113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        submitTasksIfAllowed();
158113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    }
159113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin}
160