Home | History | Annotate | Download | only in wtf
      1 /*
      2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "MainThread.h"
     31 
     32 #include "CurrentTime.h"
     33 #include "Deque.h"
     34 #include "StdLibExtras.h"
     35 #include "Threading.h"
     36 
     37 #if PLATFORM(CHROMIUM)
     38 #error Chromium uses a different main thread implementation
     39 #endif
     40 
     41 namespace WTF {
     42 
     43 struct FunctionWithContext {
     44     MainThreadFunction* function;
     45     void* context;
     46     ThreadCondition* syncFlag;
     47 
     48     FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0)
     49         : function(function)
     50         , context(context)
     51         , syncFlag(syncFlag)
     52     {
     53     }
     54     bool operator == (const FunctionWithContext& o)
     55     {
     56         return function == o.function
     57             && context == o.context
     58             && syncFlag == o.syncFlag;
     59     }
     60 };
     61 
     62 class FunctionWithContextFinder {
     63 public:
     64     FunctionWithContextFinder(const FunctionWithContext& m) : m(m) {}
     65     bool operator() (FunctionWithContext& o) { return o == m; }
     66     FunctionWithContext m;
     67 };
     68 
     69 
     70 typedef Deque<FunctionWithContext> FunctionQueue;
     71 
     72 static bool callbacksPaused; // This global variable is only accessed from main thread.
     73 #if !PLATFORM(MAC) && !PLATFORM(QT)
     74 static ThreadIdentifier mainThreadIdentifier;
     75 #endif
     76 
     77 static Mutex& mainThreadFunctionQueueMutex()
     78 {
     79     DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
     80     return staticMutex;
     81 }
     82 
     83 static FunctionQueue& functionQueue()
     84 {
     85     DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ());
     86     return staticFunctionQueue;
     87 }
     88 
     89 
     90 #if !PLATFORM(MAC)
     91 
     92 void initializeMainThread()
     93 {
     94     static bool initializedMainThread;
     95     if (initializedMainThread)
     96         return;
     97     initializedMainThread = true;
     98 
     99 #if !PLATFORM(QT)
    100     mainThreadIdentifier = currentThread();
    101 #endif
    102 
    103     mainThreadFunctionQueueMutex();
    104     initializeMainThreadPlatform();
    105 }
    106 
    107 #else
    108 
    109 static pthread_once_t initializeMainThreadKeyOnce = PTHREAD_ONCE_INIT;
    110 
    111 static void initializeMainThreadOnce()
    112 {
    113     mainThreadFunctionQueueMutex();
    114     initializeMainThreadPlatform();
    115 }
    116 
    117 void initializeMainThread()
    118 {
    119     pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadOnce);
    120 }
    121 
    122 static void initializeMainThreadToProcessMainThreadOnce()
    123 {
    124     mainThreadFunctionQueueMutex();
    125     initializeMainThreadToProcessMainThreadPlatform();
    126 }
    127 
    128 void initializeMainThreadToProcessMainThread()
    129 {
    130     pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadToProcessMainThreadOnce);
    131 }
    132 #endif
    133 
    134 // 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that.
    135 static const double maxRunLoopSuspensionTime = 0.05;
    136 
    137 void dispatchFunctionsFromMainThread()
    138 {
    139     ASSERT(isMainThread());
    140 
    141     if (callbacksPaused)
    142         return;
    143 
    144     double startTime = currentTime();
    145 
    146     FunctionWithContext invocation;
    147     while (true) {
    148         {
    149             MutexLocker locker(mainThreadFunctionQueueMutex());
    150             if (!functionQueue().size())
    151                 break;
    152             invocation = functionQueue().takeFirst();
    153         }
    154 
    155         invocation.function(invocation.context);
    156         if (invocation.syncFlag)
    157             invocation.syncFlag->signal();
    158 
    159         // If we are running accumulated functions for too long so UI may become unresponsive, we need to
    160         // yield so the user input can be processed. Otherwise user may not be able to even close the window.
    161         // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that
    162         // allows input events to be processed before we are back here.
    163         if (currentTime() - startTime > maxRunLoopSuspensionTime) {
    164             scheduleDispatchFunctionsOnMainThread();
    165             break;
    166         }
    167     }
    168 }
    169 
    170 void callOnMainThread(MainThreadFunction* function, void* context)
    171 {
    172     ASSERT(function);
    173     bool needToSchedule = false;
    174     {
    175         MutexLocker locker(mainThreadFunctionQueueMutex());
    176         needToSchedule = functionQueue().size() == 0;
    177         functionQueue().append(FunctionWithContext(function, context));
    178     }
    179     if (needToSchedule)
    180         scheduleDispatchFunctionsOnMainThread();
    181 }
    182 
    183 void callOnMainThreadAndWait(MainThreadFunction* function, void* context)
    184 {
    185     ASSERT(function);
    186 
    187     if (isMainThread()) {
    188         function(context);
    189         return;
    190     }
    191 
    192     ThreadCondition syncFlag;
    193     Mutex& functionQueueMutex = mainThreadFunctionQueueMutex();
    194     MutexLocker locker(functionQueueMutex);
    195     functionQueue().append(FunctionWithContext(function, context, &syncFlag));
    196     if (functionQueue().size() == 1)
    197         scheduleDispatchFunctionsOnMainThread();
    198     syncFlag.wait(functionQueueMutex);
    199 }
    200 
    201 void cancelCallOnMainThread(MainThreadFunction* function, void* context)
    202 {
    203     ASSERT(function);
    204 
    205     MutexLocker locker(mainThreadFunctionQueueMutex());
    206 
    207     FunctionWithContextFinder pred(FunctionWithContext(function, context));
    208 
    209     while (true) {
    210         // We must redefine 'i' each pass, because the itererator's operator=
    211         // requires 'this' to be valid, and remove() invalidates all iterators
    212         FunctionQueue::iterator i(functionQueue().findIf(pred));
    213         if (i == functionQueue().end())
    214             break;
    215         functionQueue().remove(i);
    216     }
    217 }
    218 
    219 void setMainThreadCallbacksPaused(bool paused)
    220 {
    221     ASSERT(isMainThread());
    222 
    223     if (callbacksPaused == paused)
    224         return;
    225 
    226     callbacksPaused = paused;
    227 
    228     if (!callbacksPaused)
    229         scheduleDispatchFunctionsOnMainThread();
    230 }
    231 
    232 #if !PLATFORM(MAC) && !PLATFORM(QT) && !PLATFORM(BREWMP)
    233 bool isMainThread()
    234 {
    235     return currentThread() == mainThreadIdentifier;
    236 }
    237 #endif
    238 
    239 } // namespace WTF
    240