MessageQueue.java revision 3d4e7efe37a4b0dfc5807444e8c3b98a28953377
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    /**
279     * Posts a synchronization barrier to the Looper's message queue.
280     *
281     * Message processing occurs as usual until the message queue encounters the
282     * synchronization barrier that has been posted.  When the barrier is encountered,
283     * later synchronous messages in the queue are stalled (prevented from being executed)
284     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
285     * the token that identifies the synchronization barrier.
286     *
287     * This method is used to immediately postpone execution of all subsequently posted
288     * synchronous messages until a condition is met that releases the barrier.
289     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
290     * and continue to be processed as usual.
291     *
292     * This call must be always matched by a call to {@link #removeSyncBarrier} with
293     * the same token to ensure that the message queue resumes normal operation.
294     * Otherwise the application will probably hang!
295     *
296     * @return A token that uniquely identifies the barrier.  This token must be
297     * passed to {@link #removeSyncBarrier} to release the barrier.
298     *
299     * @hide
300     */
301    public int postSyncBarrier() {
302        return postSyncBarrier(SystemClock.uptimeMillis());
303    }
304
305    private int postSyncBarrier(long when) {
306        // Enqueue a new sync barrier token.
307        // We don't need to wake the queue because the purpose of a barrier is to stall it.
308        synchronized (this) {
309            final int token = mNextBarrierToken++;
310            final Message msg = Message.obtain();
311            msg.markInUse();
312            msg.when = when;
313            msg.arg1 = token;
314
315            Message prev = null;
316            Message p = mMessages;
317            if (when != 0) {
318                while (p != null && p.when <= when) {
319                    prev = p;
320                    p = p.next;
321                }
322            }
323            if (prev != null) { // invariant: p == prev.next
324                msg.next = p;
325                prev.next = msg;
326            } else {
327                msg.next = p;
328                mMessages = msg;
329            }
330            return token;
331        }
332    }
333
334    /**
335     * Removes a synchronization barrier.
336     *
337     * @param token The synchronization barrier token that was returned by
338     * {@link #postSyncBarrier}.
339     *
340     * @throws IllegalStateException if the barrier was not found.
341     *
342     * @hide
343     */
344    public void removeSyncBarrier(int token) {
345        // Remove a sync barrier token from the queue.
346        // If the queue is no longer stalled by a barrier then wake it.
347        synchronized (this) {
348            Message prev = null;
349            Message p = mMessages;
350            while (p != null && (p.target != null || p.arg1 != token)) {
351                prev = p;
352                p = p.next;
353            }
354            if (p == null) {
355                throw new IllegalStateException("The specified message queue synchronization "
356                        + " barrier token has not been posted or has already been removed.");
357            }
358            final boolean needWake;
359            if (prev != null) {
360                prev.next = p.next;
361                needWake = false;
362            } else {
363                mMessages = p.next;
364                needWake = mMessages == null || mMessages.target != null;
365            }
366            p.recycleUnchecked();
367
368            // If the loop is quitting then it is already awake.
369            // We can assume mPtr != 0 when mQuitting is false.
370            if (needWake && !mQuitting) {
371                nativeWake(mPtr);
372            }
373        }
374    }
375
376    boolean enqueueMessage(Message msg, long when) {
377        if (msg.target == null) {
378            throw new IllegalArgumentException("Message must have a target.");
379        }
380        if (msg.isInUse()) {
381            throw new IllegalStateException(msg + " This message is already in use.");
382        }
383
384        synchronized (this) {
385            if (mQuitting) {
386                IllegalStateException e = new IllegalStateException(
387                        msg.target + " sending message to a Handler on a dead thread");
388                Log.w("MessageQueue", e.getMessage(), e);
389                msg.recycle();
390                return false;
391            }
392
393            msg.markInUse();
394            msg.when = when;
395            Message p = mMessages;
396            boolean needWake;
397            if (p == null || when == 0 || when < p.when) {
398                // New head, wake up the event queue if blocked.
399                msg.next = p;
400                mMessages = msg;
401                needWake = mBlocked;
402            } else {
403                // Inserted within the middle of the queue.  Usually we don't have to wake
404                // up the event queue unless there is a barrier at the head of the queue
405                // and the message is the earliest asynchronous message in the queue.
406                needWake = mBlocked && p.target == null && msg.isAsynchronous();
407                Message prev;
408                for (;;) {
409                    prev = p;
410                    p = p.next;
411                    if (p == null || when < p.when) {
412                        break;
413                    }
414                    if (needWake && p.isAsynchronous()) {
415                        needWake = false;
416                    }
417                }
418                msg.next = p; // invariant: p == prev.next
419                prev.next = msg;
420            }
421
422            // We can assume mPtr != 0 because mQuitting is false.
423            if (needWake) {
424                nativeWake(mPtr);
425            }
426        }
427        return true;
428    }
429
430    boolean hasMessages(Handler h, int what, Object object) {
431        if (h == null) {
432            return false;
433        }
434
435        synchronized (this) {
436            Message p = mMessages;
437            while (p != null) {
438                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
439                    return true;
440                }
441                p = p.next;
442            }
443            return false;
444        }
445    }
446
447    boolean hasMessages(Handler h, Runnable r, Object object) {
448        if (h == null) {
449            return false;
450        }
451
452        synchronized (this) {
453            Message p = mMessages;
454            while (p != null) {
455                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
456                    return true;
457                }
458                p = p.next;
459            }
460            return false;
461        }
462    }
463
464    void removeMessages(Handler h, int what, Object object) {
465        if (h == null) {
466            return;
467        }
468
469        synchronized (this) {
470            Message p = mMessages;
471
472            // Remove all messages at front.
473            while (p != null && p.target == h && p.what == what
474                   && (object == null || p.obj == object)) {
475                Message n = p.next;
476                mMessages = n;
477                p.recycleUnchecked();
478                p = n;
479            }
480
481            // Remove all messages after front.
482            while (p != null) {
483                Message n = p.next;
484                if (n != null) {
485                    if (n.target == h && n.what == what
486                        && (object == null || n.obj == object)) {
487                        Message nn = n.next;
488                        n.recycleUnchecked();
489                        p.next = nn;
490                        continue;
491                    }
492                }
493                p = n;
494            }
495        }
496    }
497
498    void removeMessages(Handler h, Runnable r, Object object) {
499        if (h == null || r == null) {
500            return;
501        }
502
503        synchronized (this) {
504            Message p = mMessages;
505
506            // Remove all messages at front.
507            while (p != null && p.target == h && p.callback == r
508                   && (object == null || p.obj == object)) {
509                Message n = p.next;
510                mMessages = n;
511                p.recycleUnchecked();
512                p = n;
513            }
514
515            // Remove all messages after front.
516            while (p != null) {
517                Message n = p.next;
518                if (n != null) {
519                    if (n.target == h && n.callback == r
520                        && (object == null || n.obj == object)) {
521                        Message nn = n.next;
522                        n.recycleUnchecked();
523                        p.next = nn;
524                        continue;
525                    }
526                }
527                p = n;
528            }
529        }
530    }
531
532    void removeCallbacksAndMessages(Handler h, Object object) {
533        if (h == null) {
534            return;
535        }
536
537        synchronized (this) {
538            Message p = mMessages;
539
540            // Remove all messages at front.
541            while (p != null && p.target == h
542                    && (object == null || p.obj == object)) {
543                Message n = p.next;
544                mMessages = n;
545                p.recycleUnchecked();
546                p = n;
547            }
548
549            // Remove all messages after front.
550            while (p != null) {
551                Message n = p.next;
552                if (n != null) {
553                    if (n.target == h && (object == null || n.obj == object)) {
554                        Message nn = n.next;
555                        n.recycleUnchecked();
556                        p.next = nn;
557                        continue;
558                    }
559                }
560                p = n;
561            }
562        }
563    }
564
565    private void removeAllMessagesLocked() {
566        Message p = mMessages;
567        while (p != null) {
568            Message n = p.next;
569            p.recycleUnchecked();
570            p = n;
571        }
572        mMessages = null;
573    }
574
575    private void removeAllFutureMessagesLocked() {
576        final long now = SystemClock.uptimeMillis();
577        Message p = mMessages;
578        if (p != null) {
579            if (p.when > now) {
580                removeAllMessagesLocked();
581            } else {
582                Message n;
583                for (;;) {
584                    n = p.next;
585                    if (n == null) {
586                        return;
587                    }
588                    if (n.when > now) {
589                        break;
590                    }
591                    p = n;
592                }
593                p.next = null;
594                do {
595                    p = n;
596                    n = p.next;
597                    p.recycleUnchecked();
598                } while (n != null);
599            }
600        }
601    }
602
603    void dump(Printer pw, String prefix) {
604        synchronized (this) {
605            long now = SystemClock.uptimeMillis();
606            int n = 0;
607            for (Message msg = mMessages; msg != null; msg = msg.next) {
608                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
609                n++;
610            }
611            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
612                    + ", quitting=" + mQuitting + ")");
613        }
614    }
615
616    /**
617     * Callback interface for discovering when a thread is going to block
618     * waiting for more messages.
619     */
620    public static interface IdleHandler {
621        /**
622         * Called when the message queue has run out of messages and will now
623         * wait for more.  Return true to keep your idle handler active, false
624         * to have it removed.  This may be called if there are still messages
625         * pending in the queue, but they are all scheduled to be dispatched
626         * after the current time.
627         */
628        boolean queueIdle();
629    }
630}
631