Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
14792 manas 1
/**
2
 * Copyright 2010-present Facebook.
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.facebook.internal;
18
 
19
import com.facebook.Settings;
20
 
21
import java.util.concurrent.Executor;
22
 
23
class WorkQueue {
24
    public static final int DEFAULT_MAX_CONCURRENT = 8;
25
 
26
    private final Object workLock = new Object();
27
    private WorkNode pendingJobs;
28
 
29
    private final int maxConcurrent;
30
    private final Executor executor;
31
 
32
    private WorkNode runningJobs = null;
33
    private int runningCount = 0;
34
 
35
    WorkQueue() {
36
        this(DEFAULT_MAX_CONCURRENT);
37
    }
38
 
39
    WorkQueue(int maxConcurrent) {
40
        this(maxConcurrent, Settings.getExecutor());
41
    }
42
 
43
    WorkQueue(int maxConcurrent, Executor executor) {
44
        this.maxConcurrent = maxConcurrent;
45
        this.executor = executor;
46
    }
47
 
48
    WorkItem addActiveWorkItem(Runnable callback) {
49
        return addActiveWorkItem(callback, true);
50
    }
51
 
52
    WorkItem addActiveWorkItem(Runnable callback, boolean addToFront) {
53
        WorkNode node = new WorkNode(callback);
54
        synchronized (workLock) {
55
            pendingJobs = node.addToList(pendingJobs, addToFront);
56
        }
57
 
58
        startItem();
59
        return node;
60
    }
61
 
62
    void validate() {
63
        synchronized (workLock) {
64
            // Verify that all running items know they are running, and counts match
65
            int count = 0;
66
 
67
            if (runningJobs != null) {
68
                WorkNode walk = runningJobs;
69
                do {
70
                    walk.verify(true);
71
                    count++;
72
                    walk = walk.getNext();
73
                } while (walk != runningJobs);
74
            }
75
 
76
            assert runningCount == count;
77
        }
78
    }
79
 
80
    private void startItem() {
81
        finishItemAndStartNew(null);
82
    }
83
 
84
    private void finishItemAndStartNew(WorkNode finished) {
85
        WorkNode ready = null;
86
 
87
        synchronized (workLock) {
88
            if (finished != null) {
89
                runningJobs = finished.removeFromList(runningJobs);
90
                runningCount--;
91
            }
92
 
93
            if (runningCount < maxConcurrent) {
94
                ready = pendingJobs; // Head of the pendingJobs queue
95
                if (ready != null) {
96
                    // The Queue reassignments are necessary since 'ready' might have been
97
                    // added / removed from the front of either queue, which changes its
98
                    // respective head.
99
                    pendingJobs = ready.removeFromList(pendingJobs);
100
                    runningJobs = ready.addToList(runningJobs, false);
101
                    runningCount++;
102
 
103
                    ready.setIsRunning(true);
104
                }
105
            }
106
        }
107
 
108
        if (ready != null) {
109
            execute(ready);
110
        }
111
    }
112
 
113
    private void execute(final WorkNode node) {
114
        executor.execute(new Runnable() {
115
            @Override
116
            public void run() {
117
                try {
118
                    node.getCallback().run();
119
                } finally {
120
                    finishItemAndStartNew(node);
121
                }
122
            }
123
        });
124
    }
125
 
126
    private class WorkNode implements WorkItem {
127
        private final Runnable callback;
128
        private WorkNode next;
129
        private WorkNode prev;
130
        private boolean isRunning;
131
 
132
        WorkNode(Runnable callback) {
133
            this.callback = callback;
134
        }
135
 
136
        @Override
137
        public boolean cancel() {
138
            synchronized (workLock) {
139
                if (!isRunning()) {
140
                    pendingJobs = removeFromList(pendingJobs);
141
                    return true;
142
                }
143
            }
144
 
145
            return false;
146
        }
147
 
148
        @Override
149
        public void moveToFront() {
150
            synchronized (workLock) {
151
                if (!isRunning()) {
152
                    pendingJobs = removeFromList(pendingJobs);
153
                    pendingJobs = addToList(pendingJobs, true);
154
                }
155
            }
156
        }
157
 
158
        @Override
159
        public boolean isRunning() {
160
            return isRunning;
161
        }
162
 
163
        Runnable getCallback() {
164
            return callback;
165
        }
166
 
167
        WorkNode getNext() {
168
            return next;
169
        }
170
 
171
        void setIsRunning(boolean isRunning) {
172
            this.isRunning = isRunning;
173
        }
174
 
175
        WorkNode addToList(WorkNode list, boolean addToFront) {
176
            assert next == null;
177
            assert prev == null;
178
 
179
            if (list == null) {
180
                list = next = prev = this;
181
            } else {
182
                next = list;
183
                prev = list.prev;
184
                next.prev = prev.next = this;
185
            }
186
 
187
            return addToFront ? this : list;
188
        }
189
 
190
        WorkNode removeFromList(WorkNode list) {
191
            assert next != null;
192
            assert prev != null;
193
 
194
            if (list == this) {
195
                if (next == this) {
196
                    list = null;
197
                } else {
198
                    list = next;
199
                }
200
            }
201
 
202
            next.prev = prev;
203
            prev.next = next;
204
            next = prev = null;
205
 
206
            return list;
207
        }
208
 
209
        void verify(boolean shouldBeRunning) {
210
            assert prev.next == this;
211
            assert next.prev == this;
212
            assert isRunning() == shouldBeRunning;
213
        }
214
    }
215
 
216
    interface WorkItem {
217
        boolean cancel();
218
        boolean isRunning();
219
        void moveToFront();
220
    }
221
}