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