| 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 "StdLibExtras.h" | 
| 33 | #include "CurrentTime.h" | 
| 34 | #include "Deque.h" | 
| 35 | #include "Threading.h" | 
| 36 |  | 
| 37 | namespace WTF { | 
| 38 |  | 
| 39 | struct FunctionWithContext { | 
| 40 |     MainThreadFunction* function; | 
| 41 |     void* context; | 
| 42 |     ThreadCondition* syncFlag; | 
| 43 |  | 
| 44 |     FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0) | 
| 45 |         : function(function) | 
| 46 |         , context(context) | 
| 47 |         , syncFlag(syncFlag) | 
| 48 |     {  | 
| 49 |     } | 
| 50 | }; | 
| 51 |  | 
| 52 | typedef Deque<FunctionWithContext> FunctionQueue; | 
| 53 |  | 
| 54 | static bool callbacksPaused; // This global variable is only accessed from main thread. | 
| 55 |  | 
| 56 | Mutex& mainThreadFunctionQueueMutex() | 
| 57 | { | 
| 58 |     DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); | 
| 59 |     return staticMutex; | 
| 60 | } | 
| 61 |  | 
| 62 | static FunctionQueue& functionQueue() | 
| 63 | { | 
| 64 |     DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ()); | 
| 65 |     return staticFunctionQueue; | 
| 66 | } | 
| 67 |  | 
| 68 | void initializeMainThread() | 
| 69 | { | 
| 70 |     mainThreadFunctionQueueMutex(); | 
| 71 |     initializeMainThreadPlatform(); | 
| 72 | } | 
| 73 |  | 
| 74 | // 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that. | 
| 75 | static const double maxRunLoopSuspensionTime = 0.05; | 
| 76 |  | 
| 77 | void dispatchFunctionsFromMainThread() | 
| 78 | { | 
| 79 |     ASSERT(isMainThread()); | 
| 80 |  | 
| 81 |     if (callbacksPaused) | 
| 82 |         return; | 
| 83 |  | 
| 84 |     double startTime = currentTime(); | 
| 85 |  | 
| 86 |     FunctionWithContext invocation; | 
| 87 |     while (true) { | 
| 88 |         { | 
| 89 |             MutexLocker locker(mainThreadFunctionQueueMutex()); | 
| 90 |             if (!functionQueue().size()) | 
| 91 |                 break; | 
| 92 |             invocation = functionQueue().first(); | 
| 93 |             functionQueue().removeFirst(); | 
| 94 |         } | 
| 95 |  | 
| 96 |         invocation.function(invocation.context); | 
| 97 |         if (invocation.syncFlag) | 
| 98 |             invocation.syncFlag->signal(); | 
| 99 |  | 
| 100 |         // If we are running accumulated functions for too long so UI may become unresponsive, we need to | 
| 101 |         // yield so the user input can be processed. Otherwise user may not be able to even close the window. | 
| 102 |         // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that | 
| 103 |         // allows input events to be processed before we are back here. | 
| 104 |         if (currentTime() - startTime > maxRunLoopSuspensionTime) { | 
| 105 |             scheduleDispatchFunctionsOnMainThread(); | 
| 106 |             break; | 
| 107 |         } | 
| 108 |     } | 
| 109 | } | 
| 110 |  | 
| 111 | void callOnMainThread(MainThreadFunction* function, void* context) | 
| 112 | { | 
| 113 |     ASSERT(function); | 
| 114 |     bool needToSchedule = false; | 
| 115 |     { | 
| 116 |         MutexLocker locker(mainThreadFunctionQueueMutex()); | 
| 117 |         needToSchedule = functionQueue().size() == 0; | 
| 118 |         functionQueue().append(value: FunctionWithContext(function, context)); | 
| 119 |     } | 
| 120 |     if (needToSchedule) | 
| 121 |         scheduleDispatchFunctionsOnMainThread(); | 
| 122 | } | 
| 123 |  | 
| 124 | void callOnMainThreadAndWait(MainThreadFunction* function, void* context) | 
| 125 | { | 
| 126 |     ASSERT(function); | 
| 127 |  | 
| 128 |     if (isMainThread()) { | 
| 129 |         function(context); | 
| 130 |         return; | 
| 131 |     } | 
| 132 |  | 
| 133 |     ThreadCondition syncFlag; | 
| 134 |     Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); | 
| 135 |     MutexLocker locker(functionQueueMutex); | 
| 136 |     functionQueue().append(value: FunctionWithContext(function, context, &syncFlag)); | 
| 137 |     if (functionQueue().size() == 1) | 
| 138 |         scheduleDispatchFunctionsOnMainThread(); | 
| 139 |     syncFlag.wait(mutex&: functionQueueMutex); | 
| 140 | } | 
| 141 |  | 
| 142 | void setMainThreadCallbacksPaused(bool paused) | 
| 143 | { | 
| 144 |     ASSERT(isMainThread()); | 
| 145 |  | 
| 146 |     if (callbacksPaused == paused) | 
| 147 |         return; | 
| 148 |  | 
| 149 |     callbacksPaused = paused; | 
| 150 |  | 
| 151 |     if (!callbacksPaused) | 
| 152 |         scheduleDispatchFunctionsOnMainThread(); | 
| 153 | } | 
| 154 |  | 
| 155 | } // namespace WTF | 
| 156 |  |