MessageQueue.java revision b1eb620219de034b28a0130b07ca05140c4ff830
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 int 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 int nativeInit();
53    private native static void nativeDestroy(int ptr);
54    private native static void nativePollOnce(int ptr, int timeoutMillis);
55    private native static void nativeWake(int ptr);
56    private native static boolean nativeIsIdling(int 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        // Return here if the message loop has already quit and been disposed.
130        // This can happen if the application tries to restart a looper after quit
131        // which is not supported.
132        final int ptr = mPtr;
133        if (ptr == 0) {
134            return null;
135        }
136
137        int pendingIdleHandlerCount = -1; // -1 only during first iteration
138        int nextPollTimeoutMillis = 0;
139        for (;;) {
140            if (nextPollTimeoutMillis != 0) {
141                Binder.flushPendingCommands();
142            }
143
144            nativePollOnce(ptr, nextPollTimeoutMillis);
145
146            synchronized (this) {
147                // Try to retrieve the next message.  Return if found.
148                final long now = SystemClock.uptimeMillis();
149                Message prevMsg = null;
150                Message msg = mMessages;
151                if (msg != null && msg.target == null) {
152                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
153                    do {
154                        prevMsg = msg;
155                        msg = msg.next;
156                    } while (msg != null && !msg.isAsynchronous());
157                }
158                if (msg != null) {
159                    if (now < msg.when) {
160                        // Next message is not ready.  Set a timeout to wake up when it is ready.
161                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
162                    } else {
163                        // Got a message.
164                        mBlocked = false;
165                        if (prevMsg != null) {
166                            prevMsg.next = msg.next;
167                        } else {
168                            mMessages = msg.next;
169                        }
170                        msg.next = null;
171                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
172                        msg.markInUse();
173                        return msg;
174                    }
175                } else {
176                    // No more messages.
177                    nextPollTimeoutMillis = -1;
178                }
179
180                // Process the quit message now that all pending messages have been handled.
181                if (mQuitting) {
182                    dispose();
183                    return null;
184                }
185
186                // If first time idle, then get the number of idlers to run.
187                // Idle handles only run if the queue is empty or if the first message
188                // in the queue (possibly a barrier) is due to be handled in the future.
189                if (pendingIdleHandlerCount < 0
190                        && (mMessages == null || now < mMessages.when)) {
191                    pendingIdleHandlerCount = mIdleHandlers.size();
192                }
193                if (pendingIdleHandlerCount <= 0) {
194                    // No idle handlers to run.  Loop and wait some more.
195                    mBlocked = true;
196                    continue;
197                }
198
199                if (mPendingIdleHandlers == null) {
200                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
201                }
202                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
203            }
204
205            // Run the idle handlers.
206            // We only ever reach this code block during the first iteration.
207            for (int i = 0; i < pendingIdleHandlerCount; i++) {
208                final IdleHandler idler = mPendingIdleHandlers[i];
209                mPendingIdleHandlers[i] = null; // release the reference to the handler
210
211                boolean keep = false;
212                try {
213                    keep = idler.queueIdle();
214                } catch (Throwable t) {
215                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
216                }
217
218                if (!keep) {
219                    synchronized (this) {
220                        mIdleHandlers.remove(idler);
221                    }
222                }
223            }
224
225            // Reset the idle handler count to 0 so we do not run them again.
226            pendingIdleHandlerCount = 0;
227
228            // While calling an idle handler, a new message could have been delivered
229            // so go back and look again for a pending message without waiting.
230            nextPollTimeoutMillis = 0;
231        }
232    }
233
234    void quit(boolean safe) {
235        if (!mQuitAllowed) {
236            throw new RuntimeException("Main thread not allowed to quit.");
237        }
238
239        synchronized (this) {
240            if (mQuitting) {
241                return;
242            }
243            mQuitting = true;
244
245            if (safe) {
246                removeAllFutureMessagesLocked();
247            } else {
248                removeAllMessagesLocked();
249            }
250
251            // We can assume mPtr != 0 because mQuitting was previously false.
252            nativeWake(mPtr);
253        }
254    }
255
256    int enqueueSyncBarrier(long when) {
257        // Enqueue a new sync barrier token.
258        // We don't need to wake the queue because the purpose of a barrier is to stall it.
259        synchronized (this) {
260            final int token = mNextBarrierToken++;
261            final Message msg = Message.obtain();
262            msg.when = when;
263            msg.arg1 = token;
264
265            Message prev = null;
266            Message p = mMessages;
267            if (when != 0) {
268                while (p != null && p.when <= when) {
269                    prev = p;
270                    p = p.next;
271                }
272            }
273            if (prev != null) { // invariant: p == prev.next
274                msg.next = p;
275                prev.next = msg;
276            } else {
277                msg.next = p;
278                mMessages = msg;
279            }
280            return token;
281        }
282    }
283
284    void removeSyncBarrier(int token) {
285        // Remove a sync barrier token from the queue.
286        // If the queue is no longer stalled by a barrier then wake it.
287        synchronized (this) {
288            Message prev = null;
289            Message p = mMessages;
290            while (p != null && (p.target != null || p.arg1 != token)) {
291                prev = p;
292                p = p.next;
293            }
294            if (p == null) {
295                throw new IllegalStateException("The specified message queue synchronization "
296                        + " barrier token has not been posted or has already been removed.");
297            }
298            final boolean needWake;
299            if (prev != null) {
300                prev.next = p.next;
301                needWake = false;
302            } else {
303                mMessages = p.next;
304                needWake = mMessages == null || mMessages.target != null;
305            }
306            p.recycle();
307
308            // If the loop is quitting then it is already awake.
309            // We can assume mPtr != 0 when mQuitting is false.
310            if (needWake && !mQuitting) {
311                nativeWake(mPtr);
312            }
313        }
314    }
315
316    boolean enqueueMessage(Message msg, long when) {
317        if (msg.isInUse()) {
318            throw new AndroidRuntimeException(msg + " This message is already in use.");
319        }
320        if (msg.target == null) {
321            throw new AndroidRuntimeException("Message must have a target.");
322        }
323
324        synchronized (this) {
325            if (mQuitting) {
326                RuntimeException e = new RuntimeException(
327                        msg.target + " sending message to a Handler on a dead thread");
328                Log.w("MessageQueue", e.getMessage(), e);
329                return false;
330            }
331
332            msg.when = when;
333            Message p = mMessages;
334            boolean needWake;
335            if (p == null || when == 0 || when < p.when) {
336                // New head, wake up the event queue if blocked.
337                msg.next = p;
338                mMessages = msg;
339                needWake = mBlocked;
340            } else {
341                // Inserted within the middle of the queue.  Usually we don't have to wake
342                // up the event queue unless there is a barrier at the head of the queue
343                // and the message is the earliest asynchronous message in the queue.
344                needWake = mBlocked && p.target == null && msg.isAsynchronous();
345                Message prev;
346                for (;;) {
347                    prev = p;
348                    p = p.next;
349                    if (p == null || when < p.when) {
350                        break;
351                    }
352                    if (needWake && p.isAsynchronous()) {
353                        needWake = false;
354                    }
355                }
356                msg.next = p; // invariant: p == prev.next
357                prev.next = msg;
358            }
359
360            // We can assume mPtr != 0 because mQuitting is false.
361            if (needWake) {
362                nativeWake(mPtr);
363            }
364        }
365        return true;
366    }
367
368    boolean hasMessages(Handler h, int what, Object object) {
369        if (h == null) {
370            return false;
371        }
372
373        synchronized (this) {
374            Message p = mMessages;
375            while (p != null) {
376                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
377                    return true;
378                }
379                p = p.next;
380            }
381            return false;
382        }
383    }
384
385    boolean hasMessages(Handler h, Runnable r, Object object) {
386        if (h == null) {
387            return false;
388        }
389
390        synchronized (this) {
391            Message p = mMessages;
392            while (p != null) {
393                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
394                    return true;
395                }
396                p = p.next;
397            }
398            return false;
399        }
400    }
401
402    boolean isIdling() {
403        synchronized (this) {
404            return isIdlingLocked();
405        }
406    }
407
408    private boolean isIdlingLocked() {
409        // If the loop is quitting then it must not be idling.
410        // We can assume mPtr != 0 when mQuitting is false.
411        return !mQuitting && nativeIsIdling(mPtr);
412     }
413
414    void removeMessages(Handler h, int what, Object object) {
415        if (h == null) {
416            return;
417        }
418
419        synchronized (this) {
420            Message p = mMessages;
421
422            // Remove all messages at front.
423            while (p != null && p.target == h && p.what == what
424                   && (object == null || p.obj == object)) {
425                Message n = p.next;
426                mMessages = n;
427                p.recycle();
428                p = n;
429            }
430
431            // Remove all messages after front.
432            while (p != null) {
433                Message n = p.next;
434                if (n != null) {
435                    if (n.target == h && n.what == what
436                        && (object == null || n.obj == object)) {
437                        Message nn = n.next;
438                        n.recycle();
439                        p.next = nn;
440                        continue;
441                    }
442                }
443                p = n;
444            }
445        }
446    }
447
448    void removeMessages(Handler h, Runnable r, Object object) {
449        if (h == null || r == null) {
450            return;
451        }
452
453        synchronized (this) {
454            Message p = mMessages;
455
456            // Remove all messages at front.
457            while (p != null && p.target == h && p.callback == r
458                   && (object == null || p.obj == object)) {
459                Message n = p.next;
460                mMessages = n;
461                p.recycle();
462                p = n;
463            }
464
465            // Remove all messages after front.
466            while (p != null) {
467                Message n = p.next;
468                if (n != null) {
469                    if (n.target == h && n.callback == r
470                        && (object == null || n.obj == object)) {
471                        Message nn = n.next;
472                        n.recycle();
473                        p.next = nn;
474                        continue;
475                    }
476                }
477                p = n;
478            }
479        }
480    }
481
482    void removeCallbacksAndMessages(Handler h, Object object) {
483        if (h == null) {
484            return;
485        }
486
487        synchronized (this) {
488            Message p = mMessages;
489
490            // Remove all messages at front.
491            while (p != null && p.target == h
492                    && (object == null || p.obj == object)) {
493                Message n = p.next;
494                mMessages = n;
495                p.recycle();
496                p = n;
497            }
498
499            // Remove all messages after front.
500            while (p != null) {
501                Message n = p.next;
502                if (n != null) {
503                    if (n.target == h && (object == null || n.obj == object)) {
504                        Message nn = n.next;
505                        n.recycle();
506                        p.next = nn;
507                        continue;
508                    }
509                }
510                p = n;
511            }
512        }
513    }
514
515    private void removeAllMessagesLocked() {
516        Message p = mMessages;
517        while (p != null) {
518            Message n = p.next;
519            p.recycle();
520            p = n;
521        }
522        mMessages = null;
523    }
524
525    private void removeAllFutureMessagesLocked() {
526        final long now = SystemClock.uptimeMillis();
527        Message p = mMessages;
528        if (p != null) {
529            if (p.when > now) {
530                removeAllMessagesLocked();
531            } else {
532                Message n;
533                for (;;) {
534                    n = p.next;
535                    if (n == null) {
536                        return;
537                    }
538                    if (n.when > now) {
539                        break;
540                    }
541                    p = n;
542                }
543                p.next = null;
544                do {
545                    p = n;
546                    n = p.next;
547                    p.recycle();
548                } while (n != null);
549            }
550        }
551    }
552
553    void dump(Printer pw, String prefix) {
554        synchronized (this) {
555            long now = SystemClock.uptimeMillis();
556            int n = 0;
557            for (Message msg = mMessages; msg != null; msg = msg.next) {
558                pw.println(prefix + "Message " + n + ": " + msg.toString(now));
559                n++;
560            }
561            pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked()
562                    + ", quitting=" + mQuitting + ")");
563        }
564    }
565}
566