MessageQueue.java revision 6c7b41adf9e937a66880b8906389760f3fc82a08
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.Log;
20import android.util.Printer;
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 long 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 mQuitting;
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 long nativeInit();
52    private native static void nativeDestroy(long ptr);
53    private native static void nativePollOnce(long ptr, int timeoutMillis);
54    private native static void nativeWake(long ptr);
55    private native static boolean nativeIsPolling(long ptr);
56
57    MessageQueue(boolean quitAllowed) {
58        mQuitAllowed = quitAllowed;
59        mPtr = nativeInit();
60    }
61
62    @Override
63    protected void finalize() throws Throwable {
64        try {
65            dispose();
66        } finally {
67            super.finalize();
68        }
69    }
70
71    // Disposes of the underlying message queue.
72    // Must only be called on the looper thread or the finalizer.
73    private void dispose() {
74        if (mPtr != 0) {
75            nativeDestroy(mPtr);
76            mPtr = 0;
77        }
78    }
79
80    /**
81     * Returns true if the looper has no pending messages which are due to be processed.
82     *
83     * <p>This method is safe to call from any thread.
84     *
85     * @return True if the looper is idle.
86     */
87    public boolean isIdle() {
88        synchronized (this) {
89            final long now = SystemClock.uptimeMillis();
90            return mMessages == null || now < mMessages.when;
91        }
92    }
93
94    /**
95     * Add a new {@link IdleHandler} to this message queue.  This may be
96     * removed automatically for you by returning false from
97     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
98     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
99     *
100     * <p>This method is safe to call from any thread.
101     *
102     * @param handler The IdleHandler to be added.
103     */
104    public void addIdleHandler(IdleHandler handler) {
105        if (handler == null) {
106            throw new NullPointerException("Can't add a null IdleHandler");
107        }
108        synchronized (this) {
109            mIdleHandlers.add(handler);
110        }
111    }
112
113    /**
114     * Remove an {@link IdleHandler} from the queue that was previously added
115     * with {@link #addIdleHandler}.  If the given object is not currently
116     * in the idle list, nothing is done.
117     *
118     * <p>This method is safe to call from any thread.
119     *
120     * @param handler The IdleHandler to be removed.
121     */
122    public void removeIdleHandler(IdleHandler handler) {
123        synchronized (this) {
124            mIdleHandlers.remove(handler);
125        }
126    }
127
128    /**
129     * Returns whether this looper's thread is currently polling for more work to do.
130     * This is a good signal that the loop is still alive rather than being stuck
131     * handling a callback.  Note that this method is intrinsically racy, since the
132     * state of the loop can change before you get the result back.
133     *
134     * <p>This method is safe to call from any thread.
135     *
136     * @return True if the looper is currently polling for events.
137     * @hide
138     */
139    public boolean isPolling() {
140        synchronized (this) {
141            return isPollingLocked();
142        }
143    }
144
145    private boolean isPollingLocked() {
146        // If the loop is quitting then it must not be idling.
147        // We can assume mPtr != 0 when mQuitting is false.
148        return !mQuitting && nativeIsPolling(mPtr);
149    }
150
151    Message next() {
152        // Return here if the message loop has already quit and been disposed.
153        // This can happen if the application tries to restart a looper after quit
154        // which is not supported.
155        final long ptr = mPtr;
156        if (ptr == 0) {
157            return null;
158        }
159
160        int pendingIdleHandlerCount = -1; // -1 only during first iteration
161        int nextPollTimeoutMillis = 0;
162        for (;;) {
163            if (nextPollTimeoutMillis != 0) {
164                Binder.flushPendingCommands();
165            }
166
167            nativePollOnce(ptr, nextPollTimeoutMillis);
168
169            synchronized (this) {
170                // Try to retrieve the next message.  Return if found.
171                final long now = SystemClock.uptimeMillis();
172                Message prevMsg = null;
173                Message msg = mMessages;
174                if (msg != null && msg.target == null) {
175                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
176                    do {
177                        prevMsg = msg;
178                        msg = msg.next;
179                    } while (msg != null && !msg.isAsynchronous());
180                }
181                if (msg != null) {
182                    if (now < msg.when) {
183                        // Next message is not ready.  Set a timeout to wake up when it is ready.
184                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
185                    } else {
186                        // Got a message.
187                        mBlocked = false;
188                        if (prevMsg != null) {
189                            prevMsg.next = msg.next;
190                        } else {
191                            mMessages = msg.next;
192                        }
193                        msg.next = null;
194                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
195                        return msg;
196                    }
197                } else {
198                    // No more messages.
199                    nextPollTimeoutMillis = -1;
200                }
201
202                // Process the quit message now that all pending messages have been handled.
203                if (mQuitting) {
204                    dispose();
205                    return null;
206                }
207
208                // If first time idle, then get the number of idlers to run.
209                // Idle handles only run if the queue is empty or if the first message
210                // in the queue (possibly a barrier) is due to be handled in the future.
211                if (pendingIdleHandlerCount < 0
212                        && (mMessages == null || now < mMessages.when)) {
213                    pendingIdleHandlerCount = mIdleHandlers.size();
214                }
215                if (pendingIdleHandlerCount <= 0) {
216                    // No idle handlers to run.  Loop and wait some more.
217                    mBlocked = true;
218                    continue;
219                }
220
221                if (mPendingIdleHandlers == null) {
222                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
223                }
224                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
225            }
226
227            // Run the idle handlers.
228            // We only ever reach this code block during the first iteration.
229            for (int i = 0; i < pendingIdleHandlerCount; i++) {
230                final IdleHandler idler = mPendingIdleHandlers[i];
231                mPendingIdleHandlers[i] = null; // release the reference to the handler
232
233                boolean keep = false;
234                try {
235                    keep = idler.queueIdle();
236                } catch (Throwable t) {
237                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
238                }
239
240                if (!keep) {
241                    synchronized (this) {
242                        mIdleHandlers.remove(idler);
243                    }
244                }
245            }
246
247            // Reset the idle handler count to 0 so we do not run them again.
248            pendingIdleHandlerCount = 0;
249
250            // While calling an idle handler, a new message could have been delivered
251            // so go back and look again for a pending message without waiting.
252            nextPollTimeoutMillis = 0;
253        }
254    }
255
256    void quit(boolean safe) {
257        if (!mQuitAllowed) {
258            throw new IllegalStateException("Main thread not allowed to quit.");
259        }
260
261        synchronized (this) {
262            if (mQuitting) {
263                return;
264            }
265            mQuitting = true;
266
267            if (safe) {
268                removeAllFutureMessagesLocked();
269            } else {
270                removeAllMessagesLocked();
271            }
272
273            // We can assume mPtr != 0 because mQuitting was previously false.
274            nativeWake(mPtr);
275        }
276    }
277
278    int enqueueSyncBarrier(long when) {
279        // Enqueue a new sync barrier token.
280        // We don't need to wake the queue because the purpose of a barrier is to stall it.
281        synchronized (this) {
282            final int token = mNextBarrierToken++;
283            final Message msg = Message.obtain();
284            msg.markInUse();
285            msg.when = when;
286            msg.arg1 = token;
287
288            Message prev = null;
289            Message p = mMessages;
290            if (when != 0) {
291                while (p != null && p.when <= when) {
292                    prev = p;
293                    p = p.next;
294                }
295            }
296            if (prev != null) { // invariant: p == prev.next
297                msg.next = p;
298                prev.next = msg;
299            } else {
300                msg.next = p;
301                mMessages = msg;
302            }
303            return token;
304        }
305    }
306
307    void removeSyncBarrier(int token) {
308        // Remove a sync barrier token from the queue.
309        // If the queue is no longer stalled by a barrier then wake it.
310        synchronized (this) {
311            Message prev = null;
312            Message p = mMessages;
313            while (p != null && (p.target != null || p.arg1 != token)) {
314                prev = p;
315                p = p.next;
316            }
317            if (p == null) {
318                throw new IllegalStateException("The specified message queue synchronization "
319                        + " barrier token has not been posted or has already been removed.");
320            }
321            final boolean needWake;
322            if (prev != null) {
323                prev.next = p.next;
324                needWake = false;
325            } else {
326                mMessages = p.next;
327                needWake = mMessages == null || mMessages.target != null;
328            }
329            p.recycleUnchecked();
330
331            // If the loop is quitting then it is already awake.
332            // We can assume mPtr != 0 when mQuitting is false.
333            if (needWake && !mQuitting) {
334                nativeWake(mPtr);
335            }
336        }
337    }
338
339    boolean enqueueMessage(Message msg, long when) {
340        if (msg.target == null) {
341            throw new IllegalArgumentException("Message must have a target.");
342        }
343        if (msg.isInUse()) {
344            throw new IllegalStateException(msg + " This message is already in use.");
345        }
346
347        synchronized (this) {
348            if (mQuitting) {
349                IllegalStateException e = new IllegalStateException(
350                        msg.target + " sending message to a Handler on a dead thread");
351                Log.w("MessageQueue", e.getMessage(), e);
352                msg.recycle();
353                return false;
354            }
355
356            msg.markInUse();
357            msg.when = when;
358            Message p = mMessages;
359            boolean needWake;
360            if (p == null || when == 0 || when < p.when) {
361                // New head, wake up the event queue if blocked.
362                msg.next = p;
363                mMessages = msg;
364                needWake = mBlocked;
365            } else {
366                // Inserted within the middle of the queue.  Usually we don't have to wake
367                // up the event queue unless there is a barrier at the head of the queue
368                // and the message is the earliest asynchronous message in the queue.
369                needWake = mBlocked && p.target == null && msg.isAsynchronous();
370                Message prev;
371                for (;;) {
372                    prev = p;
373                    p = p.next;
374                    if (p == null || when < p.when) {
375                        break;
376                    }
377                    if (needWake && p.isAsynchronous()) {
378                        needWake = false;
379                    }
380                }
381                msg.next = p; // invariant: p == prev.next
382                prev.next = msg;
383            }
384
385            // We can assume mPtr != 0 because mQuitting is false.
386            if (needWake) {
387                nativeWake(mPtr);
388            }
389        }
390        return true;
391    }
392
393    boolean hasMessages(Handler h, int what, Object object) {
394        if (h == null) {
395            return false;
396        }
397
398        synchronized (this) {
399            Message p = mMessages;
400            while (p != null) {
401                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
402                    return true;
403                }
404                p = p.next;
405            }
406            return false;
407        }
408    }
409
410    boolean hasMessages(Handler h, Runnable r, Object object) {
411        if (h == null) {
412            return false;
413        }
414
415        synchronized (this) {
416            Message p = mMessages;
417            while (p != null) {
418                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
419                    return true;
420                }
421                p = p.next;
422            }
423            return false;
424        }
425    }
426
427    void removeMessages(Handler h, int what, Object object) {
428        if (h == null) {
429            return;
430        }
431
432        synchronized (this) {
433            Message p = mMessages;
434
435            // Remove all messages at front.
436            while (p != null && p.target == h && p.what == what
437                   && (object == null || p.obj == object)) {
438                Message n = p.next;
439                mMessages = n;
440                p.recycleUnchecked();
441                p = n;
442            }
443
444            // Remove all messages after front.
445            while (p != null) {
446                Message n = p.next;
447                if (n != null) {
448                    if (n.target == h && n.what == what
449                        && (object == null || n.obj == object)) {
450                        Message nn = n.next;
451                        n.recycleUnchecked();
452                        p.next = nn;
453                        continue;
454                    }
455                }
456                p = n;
457            }
458        }
459    }
460
461    void removeMessages(Handler h, Runnable r, Object object) {
462        if (h == null || r == null) {
463            return;
464        }
465
466        synchronized (this) {
467            Message p = mMessages;
468
469            // Remove all messages at front.
470            while (p != null && p.target == h && p.callback == r
471                   && (object == null || p.obj == object)) {
472                Message n = p.next;
473                mMessages = n;
474                p.recycleUnchecked();
475                p = n;
476            }
477
478            // Remove all messages after front.
479            while (p != null) {
480                Message n = p.next;
481                if (n != null) {
482                    if (n.target == h && n.callback == r
483                        && (object == null || n.obj == object)) {
484                        Message nn = n.next;
485                        n.recycleUnchecked();
486                        p.next = nn;
487                        continue;
488                    }
489                }
490                p = n;
491            }
492        }
493    }
494
495    void removeCallbacksAndMessages(Handler h, Object object) {
496        if (h == null) {
497            return;
498        }
499
500        synchronized (this) {
501            Message p = mMessages;
502
503            // Remove all messages at front.
504            while (p != null && p.target == h
505                    && (object == null || p.obj == object)) {
506                Message n = p.next;
507                mMessages = n;
508                p.recycleUnchecked();
509                p = n;
510            }
511
512            // Remove all messages after front.
513            while (p != null) {
514                Message n = p.next;
515                if (n != null) {
516                    if (n.target == h && (object == null || n.obj == object)) {
517                        Message nn = n.next;
518                        n.recycleUnchecked();
519                        p.next = nn;
520                        continue;
521                    }
522                }
523                p = n;
524            }
525        }
526    }
527
528    private void removeAllMessagesLocked() {
529        Message p = mMessages;
530        while (p != null) {
531            Message n = p.next;
532            p.recycleUnchecked();
533            p = n;
534        }
535        mMessages = null;
536    }
537
538    private void removeAllFutureMessagesLocked() {
539        final long now = SystemClock.uptimeMillis();
540        Message p = mMessages;
541        if (p != null) {
542            if (p.when > now) {
543                removeAllMessagesLocked();
544            } else {
545                Message n;
546                for (;;) {
547                    n = p.next;
548                    if (n == null) {
549                        return;
550                    }
551                    if (n.when > now) {
552                        break;
553                    }
554                    p = n;
555                }
556                p.next = null;
557                do {
558                    p = n;
559                    n = p.next;
560                    p.recycleUnchecked();
561                } while (n != null);
562            }
563        }
564    }
565
566    void dump(Printer pw, String prefix) {
567        synchronized (this) {
568            long now = SystemClock.uptimeMillis();
569            int n = 0;
570            for (Message msg = mMessages; msg != null; msg = msg.next) {
571                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
572                n++;
573            }
574            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
575                    + ", quitting=" + mQuitting + ")");
576        }
577    }
578
579    /**
580     * Callback interface for discovering when a thread is going to block
581     * waiting for more messages.
582     */
583    public static interface IdleHandler {
584        /**
585         * Called when the message queue has run out of messages and will now
586         * wait for more.  Return true to keep your idle handler active, false
587         * to have it removed.  This may be called if there are still messages
588         * pending in the queue, but they are all scheduled to be dispatched
589         * after the current time.
590         */
591        boolean queueIdle();
592    }
593}
594