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