• 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 import android.util.Printer;
22 
23 import 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  */
33 public 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 
nativeInit()52     private native static int nativeInit();
nativeDestroy(int ptr)53     private native static void nativeDestroy(int ptr);
nativePollOnce(int ptr, int timeoutMillis)54     private native static void nativePollOnce(int ptr, int timeoutMillis);
nativeWake(int ptr)55     private native static void nativeWake(int ptr);
nativeIsIdling(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          */
queueIdle()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      */
addIdleHandler(IdleHandler handler)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      */
removeIdleHandler(IdleHandler handler)99     public void removeIdleHandler(IdleHandler handler) {
100         synchronized (this) {
101             mIdleHandlers.remove(handler);
102         }
103     }
104 
MessageQueue(boolean quitAllowed)105     MessageQueue(boolean quitAllowed) {
106         mQuitAllowed = quitAllowed;
107         mPtr = nativeInit();
108     }
109 
110     @Override
finalize()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.
dispose()121     private void dispose() {
122         if (mPtr != 0) {
123             nativeDestroy(mPtr);
124             mPtr = 0;
125         }
126     }
127 
next()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 
quit(boolean safe)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 
enqueueSyncBarrier(long when)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 
removeSyncBarrier(int token)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 
enqueueMessage(Message msg, long when)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 
hasMessages(Handler h, int what, Object object)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 
hasMessages(Handler h, Runnable r, Object object)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 
isIdling()396     boolean isIdling() {
397         synchronized (this) {
398             return isIdlingLocked();
399         }
400     }
401 
isIdlingLocked()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 
removeMessages(Handler h, int what, Object object)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 
removeMessages(Handler h, Runnable r, Object object)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 
removeCallbacksAndMessages(Handler h, Object object)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 
removeAllMessagesLocked()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 
removeAllFutureMessagesLocked()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 
dump(Printer pw, String prefix)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