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 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(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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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