• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.os;
18 
19 import android.util.AndroidRuntimeException;
20 import android.util.Log;
21 
22 import 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  */
32 public 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 
nativeInit()51     private native static int nativeInit();
nativeDestroy(int ptr)52     private native static void nativeDestroy(int ptr);
nativePollOnce(int ptr, int timeoutMillis)53     private native static void nativePollOnce(int ptr, int timeoutMillis);
nativeWake(int ptr)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          */
queueIdle()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      */
addIdleHandler(IdleHandler handler)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      */
removeIdleHandler(IdleHandler handler)97     public void removeIdleHandler(IdleHandler handler) {
98         synchronized (this) {
99             mIdleHandlers.remove(handler);
100         }
101     }
102 
MessageQueue(boolean quitAllowed)103     MessageQueue(boolean quitAllowed) {
104         mQuitAllowed = quitAllowed;
105         mPtr = nativeInit();
106     }
107 
108     @Override
finalize()109     protected void finalize() throws Throwable {
110         try {
111             dispose();
112         } finally {
113             super.finalize();
114         }
115     }
116 
dispose()117     private void dispose() {
118         if (mPtr != 0) {
119             nativeDestroy(mPtr);
120             mPtr = 0;
121         }
122     }
123 
next()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 
quit(boolean safe)222     void quit(boolean safe) {
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             if (safe) {
234                 removeAllFutureMessagesLocked();
235             } else {
236                 removeAllMessagesLocked();
237             }
238         }
239         nativeWake(mPtr);
240     }
241 
enqueueSyncBarrier(long when)242     int enqueueSyncBarrier(long when) {
243         // Enqueue a new sync barrier token.
244         // We don't need to wake the queue because the purpose of a barrier is to stall it.
245         synchronized (this) {
246             final int token = mNextBarrierToken++;
247             final Message msg = Message.obtain();
248             msg.arg1 = token;
249 
250             Message prev = null;
251             Message p = mMessages;
252             if (when != 0) {
253                 while (p != null && p.when <= when) {
254                     prev = p;
255                     p = p.next;
256                 }
257             }
258             if (prev != null) { // invariant: p == prev.next
259                 msg.next = p;
260                 prev.next = msg;
261             } else {
262                 msg.next = p;
263                 mMessages = msg;
264             }
265             return token;
266         }
267     }
268 
removeSyncBarrier(int token)269     void removeSyncBarrier(int token) {
270         // Remove a sync barrier token from the queue.
271         // If the queue is no longer stalled by a barrier then wake it.
272         final boolean needWake;
273         synchronized (this) {
274             Message prev = null;
275             Message p = mMessages;
276             while (p != null && (p.target != null || p.arg1 != token)) {
277                 prev = p;
278                 p = p.next;
279             }
280             if (p == null) {
281                 throw new IllegalStateException("The specified message queue synchronization "
282                         + " barrier token has not been posted or has already been removed.");
283             }
284             if (prev != null) {
285                 prev.next = p.next;
286                 needWake = false;
287             } else {
288                 mMessages = p.next;
289                 needWake = mMessages == null || mMessages.target != null;
290             }
291             p.recycle();
292         }
293         if (needWake) {
294             nativeWake(mPtr);
295         }
296     }
297 
enqueueMessage(Message msg, long when)298     boolean enqueueMessage(Message msg, long when) {
299         if (msg.isInUse()) {
300             throw new AndroidRuntimeException(msg + " This message is already in use.");
301         }
302         if (msg.target == null) {
303             throw new AndroidRuntimeException("Message must have a target.");
304         }
305 
306         boolean needWake;
307         synchronized (this) {
308             if (mQuiting) {
309                 RuntimeException e = new RuntimeException(
310                         msg.target + " sending message to a Handler on a dead thread");
311                 Log.w("MessageQueue", e.getMessage(), e);
312                 return false;
313             }
314 
315             msg.when = when;
316             Message p = mMessages;
317             if (p == null || when == 0 || when < p.when) {
318                 // New head, wake up the event queue if blocked.
319                 msg.next = p;
320                 mMessages = msg;
321                 needWake = mBlocked;
322             } else {
323                 // Inserted within the middle of the queue.  Usually we don't have to wake
324                 // up the event queue unless there is a barrier at the head of the queue
325                 // and the message is the earliest asynchronous message in the queue.
326                 needWake = mBlocked && p.target == null && msg.isAsynchronous();
327                 Message prev;
328                 for (;;) {
329                     prev = p;
330                     p = p.next;
331                     if (p == null || when < p.when) {
332                         break;
333                     }
334                     if (needWake && p.isAsynchronous()) {
335                         needWake = false;
336                     }
337                 }
338                 msg.next = p; // invariant: p == prev.next
339                 prev.next = msg;
340             }
341         }
342         if (needWake) {
343             nativeWake(mPtr);
344         }
345         return true;
346     }
347 
hasMessages(Handler h, int what, Object object)348     boolean hasMessages(Handler h, int what, Object object) {
349         if (h == null) {
350             return false;
351         }
352 
353         synchronized (this) {
354             Message p = mMessages;
355             while (p != null) {
356                 if (p.target == h && p.what == what && (object == null || p.obj == object)) {
357                     return true;
358                 }
359                 p = p.next;
360             }
361             return false;
362         }
363     }
364 
hasMessages(Handler h, Runnable r, Object object)365     boolean hasMessages(Handler h, Runnable r, Object object) {
366         if (h == null) {
367             return false;
368         }
369 
370         synchronized (this) {
371             Message p = mMessages;
372             while (p != null) {
373                 if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
374                     return true;
375                 }
376                 p = p.next;
377             }
378             return false;
379         }
380     }
381 
removeMessages(Handler h, int what, Object object)382     void removeMessages(Handler h, int what, Object object) {
383         if (h == null) {
384             return;
385         }
386 
387         synchronized (this) {
388             Message p = mMessages;
389 
390             // Remove all messages at front.
391             while (p != null && p.target == h && p.what == what
392                    && (object == null || p.obj == object)) {
393                 Message n = p.next;
394                 mMessages = n;
395                 p.recycle();
396                 p = n;
397             }
398 
399             // Remove all messages after front.
400             while (p != null) {
401                 Message n = p.next;
402                 if (n != null) {
403                     if (n.target == h && n.what == what
404                         && (object == null || n.obj == object)) {
405                         Message nn = n.next;
406                         n.recycle();
407                         p.next = nn;
408                         continue;
409                     }
410                 }
411                 p = n;
412             }
413         }
414     }
415 
removeMessages(Handler h, Runnable r, Object object)416     void removeMessages(Handler h, Runnable r, Object object) {
417         if (h == null || r == null) {
418             return;
419         }
420 
421         synchronized (this) {
422             Message p = mMessages;
423 
424             // Remove all messages at front.
425             while (p != null && p.target == h && p.callback == r
426                    && (object == null || p.obj == object)) {
427                 Message n = p.next;
428                 mMessages = n;
429                 p.recycle();
430                 p = n;
431             }
432 
433             // Remove all messages after front.
434             while (p != null) {
435                 Message n = p.next;
436                 if (n != null) {
437                     if (n.target == h && n.callback == r
438                         && (object == null || n.obj == object)) {
439                         Message nn = n.next;
440                         n.recycle();
441                         p.next = nn;
442                         continue;
443                     }
444                 }
445                 p = n;
446             }
447         }
448     }
449 
removeCallbacksAndMessages(Handler h, Object object)450     void removeCallbacksAndMessages(Handler h, Object object) {
451         if (h == null) {
452             return;
453         }
454 
455         synchronized (this) {
456             Message p = mMessages;
457 
458             // Remove all messages at front.
459             while (p != null && p.target == h
460                     && (object == null || p.obj == object)) {
461                 Message n = p.next;
462                 mMessages = n;
463                 p.recycle();
464                 p = n;
465             }
466 
467             // Remove all messages after front.
468             while (p != null) {
469                 Message n = p.next;
470                 if (n != null) {
471                     if (n.target == h && (object == null || n.obj == object)) {
472                         Message nn = n.next;
473                         n.recycle();
474                         p.next = nn;
475                         continue;
476                     }
477                 }
478                 p = n;
479             }
480         }
481     }
482 
removeAllMessagesLocked()483     private void removeAllMessagesLocked() {
484         Message p = mMessages;
485         while (p != null) {
486             Message n = p.next;
487             p.recycle();
488             p = n;
489         }
490         mMessages = null;
491     }
492 
removeAllFutureMessagesLocked()493     private void removeAllFutureMessagesLocked() {
494         final long now = SystemClock.uptimeMillis();
495         Message p = mMessages;
496         if (p != null) {
497             if (p.when > now) {
498                 removeAllMessagesLocked();
499             } else {
500                 Message n;
501                 for (;;) {
502                     n = p.next;
503                     if (n == null) {
504                         return;
505                     }
506                     if (n.when > now) {
507                         break;
508                     }
509                     p = n;
510                 }
511                 p.next = null;
512                 do {
513                     p = n;
514                     n = p.next;
515                     p.recycle();
516                 } while (n != null);
517             }
518         }
519     }
520 }
521