2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "MainThread.h"
32 #include "CurrentTime.h"
34 #include "Functional.h"
35 #include "StdLibExtras.h"
36 #include "Threading.h"
37 #include <wtf/ThreadSpecific.h>
39 #if PLATFORM(CHROMIUM)
40 #error Chromium uses a different main thread implementation
45 struct FunctionWithContext {
46 MainThreadFunction* function;
48 ThreadCondition* syncFlag;
50 FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0)
56 bool operator == (const FunctionWithContext& o)
58 return function == o.function
59 && context == o.context
60 && syncFlag == o.syncFlag;
64 class FunctionWithContextFinder {
66 FunctionWithContextFinder(const FunctionWithContext& m) : m(m) {}
67 bool operator() (FunctionWithContext& o) { return o == m; }
68 FunctionWithContext m;
72 typedef Deque<FunctionWithContext> FunctionQueue;
74 static bool callbacksPaused; // This global variable is only accessed from main thread.
76 static ThreadIdentifier mainThreadIdentifier;
79 static Mutex& mainThreadFunctionQueueMutex()
81 DEFINE_STATIC_LOCAL(Mutex, staticMutex, ());
85 static FunctionQueue& functionQueue()
87 DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ());
88 return staticFunctionQueue;
94 void initializeMainThread()
96 static bool initializedMainThread;
97 if (initializedMainThread)
99 initializedMainThread = true;
101 mainThreadIdentifier = currentThread();
103 mainThreadFunctionQueueMutex();
104 initializeMainThreadPlatform();
105 initializeGCThreads();
110 static pthread_once_t initializeMainThreadKeyOnce = PTHREAD_ONCE_INIT;
112 static void initializeMainThreadOnce()
114 mainThreadFunctionQueueMutex();
115 initializeMainThreadPlatform();
118 void initializeMainThread()
120 pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadOnce);
123 static void initializeMainThreadToProcessMainThreadOnce()
125 mainThreadFunctionQueueMutex();
126 initializeMainThreadToProcessMainThreadPlatform();
129 void initializeMainThreadToProcessMainThread()
131 pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadToProcessMainThreadOnce);
135 // 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that.
136 static const double maxRunLoopSuspensionTime = 0.05;
138 void dispatchFunctionsFromMainThread()
140 ASSERT(isMainThread());
145 double startTime = currentTime();
147 FunctionWithContext invocation;
150 MutexLocker locker(mainThreadFunctionQueueMutex());
151 if (!functionQueue().size())
153 invocation = functionQueue().takeFirst();
156 invocation.function(invocation.context);
157 if (invocation.syncFlag) {
158 MutexLocker locker(mainThreadFunctionQueueMutex());
159 invocation.syncFlag->signal();
162 // If we are running accumulated functions for too long so UI may become unresponsive, we need to
163 // yield so the user input can be processed. Otherwise user may not be able to even close the window.
164 // This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that
165 // allows input events to be processed before we are back here.
166 if (currentTime() - startTime > maxRunLoopSuspensionTime) {
167 scheduleDispatchFunctionsOnMainThread();
173 void callOnMainThread(MainThreadFunction* function, void* context)
176 bool needToSchedule = false;
178 MutexLocker locker(mainThreadFunctionQueueMutex());
179 needToSchedule = functionQueue().size() == 0;
180 functionQueue().append(FunctionWithContext(function, context));
183 scheduleDispatchFunctionsOnMainThread();
186 void callOnMainThreadAndWait(MainThreadFunction* function, void* context)
190 if (isMainThread()) {
195 ThreadCondition syncFlag;
196 Mutex& functionQueueMutex = mainThreadFunctionQueueMutex();
197 MutexLocker locker(functionQueueMutex);
198 functionQueue().append(FunctionWithContext(function, context, &syncFlag));
199 if (functionQueue().size() == 1)
200 scheduleDispatchFunctionsOnMainThread();
201 syncFlag.wait(functionQueueMutex);
204 void cancelCallOnMainThread(MainThreadFunction* function, void* context)
208 MutexLocker locker(mainThreadFunctionQueueMutex());
210 FunctionWithContextFinder pred(FunctionWithContext(function, context));
213 // We must redefine 'i' each pass, because the itererator's operator=
214 // requires 'this' to be valid, and remove() invalidates all iterators
215 FunctionQueue::iterator i(functionQueue().findIf(pred));
216 if (i == functionQueue().end())
218 functionQueue().remove(i);
222 static void callFunctionObject(void* context)
224 Function<void ()>* function = static_cast<Function<void ()>*>(context);
229 void callOnMainThread(const Function<void ()>& function)
231 callOnMainThread(callFunctionObject, new Function<void ()>(function));
234 void setMainThreadCallbacksPaused(bool paused)
236 ASSERT(isMainThread());
238 if (callbacksPaused == paused)
241 callbacksPaused = paused;
243 if (!callbacksPaused)
244 scheduleDispatchFunctionsOnMainThread();
250 return currentThread() == mainThreadIdentifier;
254 #if ENABLE(PARALLEL_GC)
255 static ThreadSpecific<bool>* isGCThread;
258 void initializeGCThreads()
260 #if ENABLE(PARALLEL_GC)
261 isGCThread = new ThreadSpecific<bool>();
265 #if ENABLE(PARALLEL_GC)
266 void registerGCThread()
269 // This happens if we're running in a process that doesn't care about
277 bool isMainThreadOrGCThread()
279 if (isGCThread->isSet() && **isGCThread)
282 return isMainThread();
285 // This is necessary because JavaScriptCore.exp doesn't support preprocessor macros.
286 bool isMainThreadOrGCThread()
288 return isMainThread();