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