MessageQueue.java revision 67fc67cf3e1dba17b0eae4f3923f3e93a78c7575
1/*
2 * Copyright (C) 2006 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 android.os;
18
19import android.util.AndroidRuntimeException;
20import android.util.Log;
21
22import java.util.ArrayList;
23
24/**
25 * Low-level class holding the list of messages to be dispatched by a
26 * {@link Looper}.  Messages are not added directly to a MessageQueue,
27 * but rather through {@link Handler} objects associated with the Looper.
28 *
29 * <p>You can retrieve the MessageQueue for the current thread with
30 * {@link Looper#myQueue() Looper.myQueue()}.
31 */
32public final class MessageQueue {
33    // True if the message queue can be quit.
34    private final boolean mQuitAllowed;
35
36    @SuppressWarnings("unused")
37    private int mPtr; // used by native code
38
39    Message mMessages;
40    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
41    private IdleHandler[] mPendingIdleHandlers;
42    private boolean mQuiting;
43
44    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
45    private boolean mBlocked;
46
47    // The next barrier token.
48    // Barriers are indicated by messages with a null target whose arg1 field carries the token.
49    private int mNextBarrierToken;
50
51    private native static int nativeInit();
52    private native static void nativeDestroy(int ptr);
53    private native static void nativePollOnce(int ptr, int timeoutMillis);
54    private native static void nativeWake(int ptr);
55
56    /**
57     * Callback interface for discovering when a thread is going to block
58     * waiting for more messages.
59     */
60    public static interface IdleHandler {
61        /**
62         * Called when the message queue has run out of messages and will now
63         * wait for more.  Return true to keep your idle handler active, false
64         * to have it removed.  This may be called if there are still messages
65         * pending in the queue, but they are all scheduled to be dispatched
66         * after the current time.
67         */
68        boolean queueIdle();
69    }
70
71    /**
72     * Add a new {@link IdleHandler} to this message queue.  This may be
73     * removed automatically for you by returning false from
74     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
75     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
76     *
77     * <p>This method is safe to call from any thread.
78     *
79     * @param handler The IdleHandler to be added.
80     */
81    public void addIdleHandler(IdleHandler handler) {
82        if (handler == null) {
83            throw new NullPointerException("Can't add a null IdleHandler");
84        }
85        synchronized (this) {
86            mIdleHandlers.add(handler);
87        }
88    }
89
90    /**
91     * Remove an {@link IdleHandler} from the queue that was previously added
92     * with {@link #addIdleHandler}.  If the given object is not currently
93     * in the idle list, nothing is done.
94     *
95     * @param handler The IdleHandler to be removed.
96     */
97    public void removeIdleHandler(IdleHandler handler) {
98        synchronized (this) {
99            mIdleHandlers.remove(handler);
100        }
101    }
102
103    MessageQueue(boolean quitAllowed) {
104        mQuitAllowed = quitAllowed;
105        mPtr = nativeInit();
106    }
107
108    @Override
109    protected void finalize() throws Throwable {
110        try {
111            dispose();
112        } finally {
113            super.finalize();
114        }
115    }
116
117    private void dispose() {
118        if (mPtr != 0) {
119            nativeDestroy(mPtr);
120            mPtr = 0;
121        }
122    }
123
124    Message next() {
125        int pendingIdleHandlerCount = -1; // -1 only during first iteration
126        int nextPollTimeoutMillis = 0;
127
128        for (;;) {
129            if (nextPollTimeoutMillis != 0) {
130                Binder.flushPendingCommands();
131            }
132            nativePollOnce(mPtr, nextPollTimeoutMillis);
133
134            synchronized (this) {
135                if (mQuiting) {
136                    dispose();
137                    return null;
138                }
139
140                // Try to retrieve the next message.  Return if found.
141                final long now = SystemClock.uptimeMillis();
142                Message prevMsg = null;
143                Message msg = mMessages;
144                if (msg != null && msg.target == null) {
145                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
146                    do {
147                        prevMsg = msg;
148                        msg = msg.next;
149                    } while (msg != null && !msg.isAsynchronous());
150                }
151                if (msg != null) {
152                    if (now < msg.when) {
153                        // Next message is not ready.  Set a timeout to wake up when it is ready.
154                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
155                    } else {
156                        // Got a message.
157                        mBlocked = false;
158                        if (prevMsg != null) {
159                            prevMsg.next = msg.next;
160                        } else {
161                            mMessages = msg.next;
162                        }
163                        msg.next = null;
164                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
165                        msg.markInUse();
166                        return msg;
167                    }
168                } else {
169                    // No more messages.
170                    nextPollTimeoutMillis = -1;
171                }
172
173                // If first time idle, then get the number of idlers to run.
174                // Idle handles only run if the queue is empty or if the first message
175                // in the queue (possibly a barrier) is due to be handled in the future.
176                if (pendingIdleHandlerCount < 0
177                        && (mMessages == null || now < mMessages.when)) {
178                    pendingIdleHandlerCount = mIdleHandlers.size();
179                }
180                if (pendingIdleHandlerCount <= 0) {
181                    // No idle handlers to run.  Loop and wait some more.
182                    mBlocked = true;
183                    continue;
184                }
185
186                if (mPendingIdleHandlers == null) {
187                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
188                }
189                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
190            }
191
192            // Run the idle handlers.
193            // We only ever reach this code block during the first iteration.
194            for (int i = 0; i < pendingIdleHandlerCount; i++) {
195                final IdleHandler idler = mPendingIdleHandlers[i];
196                mPendingIdleHandlers[i] = null; // release the reference to the handler
197
198                boolean keep = false;
199                try {
200                    keep = idler.queueIdle();
201                } catch (Throwable t) {
202                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
203                }
204
205                if (!keep) {
206                    synchronized (this) {
207                        mIdleHandlers.remove(idler);
208                    }
209                }
210            }
211
212            // Reset the idle handler count to 0 so we do not run them again.
213            pendingIdleHandlerCount = 0;
214
215            // While calling an idle handler, a new message could have been delivered
216            // so go back and look again for a pending message without waiting.
217            nextPollTimeoutMillis = 0;
218        }
219    }
220
221    void quit() {
222        if (!mQuitAllowed) {
223            throw new RuntimeException("Main thread not allowed to quit.");
224        }
225
226        synchronized (this) {
227            if (mQuiting) {
228                return;
229            }
230            mQuiting = true;
231        }
232        nativeWake(mPtr);
233    }
234
235    int enqueueSyncBarrier(long when) {
236        // Enqueue a new sync barrier token.
237        // We don't need to wake the queue because the purpose of a barrier is to stall it.
238        synchronized (this) {
239            final int token = mNextBarrierToken++;
240            final Message msg = Message.obtain();
241            msg.arg1 = token;
242
243            Message prev = null;
244            Message p = mMessages;
245            if (when != 0) {
246                while (p != null && p.when <= when) {
247                    prev = p;
248                    p = p.next;
249                }
250            }
251            if (prev != null) { // invariant: p == prev.next
252                msg.next = p;
253                prev.next = msg;
254            } else {
255                msg.next = p;
256                mMessages = msg;
257            }
258            return token;
259        }
260    }
261
262    void removeSyncBarrier(int token) {
263        // Remove a sync barrier token from the queue.
264        // If the queue is no longer stalled by a barrier then wake it.
265        final boolean needWake;
266        synchronized (this) {
267            Message prev = null;
268            Message p = mMessages;
269            while (p != null && (p.target != null || p.arg1 != token)) {
270                prev = p;
271                p = p.next;
272            }
273            if (p == null) {
274                throw new IllegalStateException("The specified message queue synchronization "
275                        + " barrier token has not been posted or has already been removed.");
276            }
277            if (prev != null) {
278                prev.next = p.next;
279                needWake = false;
280            } else {
281                mMessages = p.next;
282                needWake = mMessages == null || mMessages.target != null;
283            }
284            p.recycle();
285        }
286        if (needWake) {
287            nativeWake(mPtr);
288        }
289    }
290
291    boolean enqueueMessage(Message msg, long when) {
292        if (msg.isInUse()) {
293            throw new AndroidRuntimeException(msg + " This message is already in use.");
294        }
295        if (msg.target == null) {
296            throw new AndroidRuntimeException("Message must have a target.");
297        }
298
299        boolean needWake;
300        synchronized (this) {
301            if (mQuiting) {
302                RuntimeException e = new RuntimeException(
303                        msg.target + " sending message to a Handler on a dead thread");
304                Log.w("MessageQueue", e.getMessage(), e);
305                return false;
306            }
307
308            msg.when = when;
309            Message p = mMessages;
310            if (p == null || when == 0 || when < p.when) {
311                // New head, wake up the event queue if blocked.
312                msg.next = p;
313                mMessages = msg;
314                needWake = mBlocked;
315            } else {
316                // Inserted within the middle of the queue.  Usually we don't have to wake
317                // up the event queue unless there is a barrier at the head of the queue
318                // and the message is the earliest asynchronous message in the queue.
319                needWake = mBlocked && p.target == null && msg.isAsynchronous();
320                Message prev;
321                for (;;) {
322                    prev = p;
323                    p = p.next;
324                    if (p == null || when < p.when) {
325                        break;
326                    }
327                    if (needWake && p.isAsynchronous()) {
328                        needWake = false;
329                    }
330                }
331                msg.next = p; // invariant: p == prev.next
332                prev.next = msg;
333            }
334        }
335        if (needWake) {
336            nativeWake(mPtr);
337        }
338        return true;
339    }
340
341    boolean hasMessages(Handler h, int what, Object object) {
342        if (h == null) {
343            return false;
344        }
345
346        synchronized (this) {
347            Message p = mMessages;
348            while (p != null) {
349                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
350                    return true;
351                }
352                p = p.next;
353            }
354            return false;
355        }
356    }
357
358    boolean hasMessages(Handler h, Runnable r, Object object) {
359        if (h == null) {
360            return false;
361        }
362
363        synchronized (this) {
364            Message p = mMessages;
365            while (p != null) {
366                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
367                    return true;
368                }
369                p = p.next;
370            }
371            return false;
372        }
373    }
374
375    void removeMessages(Handler h, int what, Object object) {
376        if (h == null) {
377            return;
378        }
379
380        synchronized (this) {
381            Message p = mMessages;
382
383            // Remove all messages at front.
384            while (p != null && p.target == h && p.what == what
385                   && (object == null || p.obj == object)) {
386                Message n = p.next;
387                mMessages = n;
388                p.recycle();
389                p = n;
390            }
391
392            // Remove all messages after front.
393            while (p != null) {
394                Message n = p.next;
395                if (n != null) {
396                    if (n.target == h && n.what == what
397                        && (object == null || n.obj == object)) {
398                        Message nn = n.next;
399                        n.recycle();
400                        p.next = nn;
401                        continue;
402                    }
403                }
404                p = n;
405            }
406        }
407    }
408
409    void removeMessages(Handler h, Runnable r, Object object) {
410        if (h == null || r == null) {
411            return;
412        }
413
414        synchronized (this) {
415            Message p = mMessages;
416
417            // Remove all messages at front.
418            while (p != null && p.target == h && p.callback == r
419                   && (object == null || p.obj == object)) {
420                Message n = p.next;
421                mMessages = n;
422                p.recycle();
423                p = n;
424            }
425
426            // Remove all messages after front.
427            while (p != null) {
428                Message n = p.next;
429                if (n != null) {
430                    if (n.target == h && n.callback == r
431                        && (object == null || n.obj == object)) {
432                        Message nn = n.next;
433                        n.recycle();
434                        p.next = nn;
435                        continue;
436                    }
437                }
438                p = n;
439            }
440        }
441    }
442
443    void removeCallbacksAndMessages(Handler h, Object object) {
444        if (h == null) {
445            return;
446        }
447
448        synchronized (this) {
449            Message p = mMessages;
450
451            // Remove all messages at front.
452            while (p != null && p.target == h
453                    && (object == null || p.obj == object)) {
454                Message n = p.next;
455                mMessages = n;
456                p.recycle();
457                p = n;
458            }
459
460            // Remove all messages after front.
461            while (p != null) {
462                Message n = p.next;
463                if (n != null) {
464                    if (n.target == h && (object == null || n.obj == object)) {
465                        Message nn = n.next;
466                        n.recycle();
467                        p.next = nn;
468                        continue;
469                    }
470                }
471                p = n;
472            }
473        }
474    }
475}
476