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