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