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