/* | |
* Copyright (C) 2007, 2008 Apple Inc. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of | |
* its contributors may be used to endorse or promote products derived | |
* from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include "config.h" | |
#include "MainThread.h" | |
#include "StdLibExtras.h" | |
#include "CurrentTime.h" | |
#include "Deque.h" | |
#include "Threading.h" | |
namespace WTF { | |
struct FunctionWithContext { | |
MainThreadFunction* function; | |
void* context; | |
ThreadCondition* syncFlag; | |
FunctionWithContext(MainThreadFunction* function = 0, void* context = 0, ThreadCondition* syncFlag = 0) | |
: function(function) | |
, context(context) | |
, syncFlag(syncFlag) | |
{ | |
} | |
}; | |
typedef Deque<FunctionWithContext> FunctionQueue; | |
static bool callbacksPaused; // This global variable is only accessed from main thread. | |
Mutex& mainThreadFunctionQueueMutex() | |
{ | |
DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); | |
return staticMutex; | |
} | |
static FunctionQueue& functionQueue() | |
{ | |
DEFINE_STATIC_LOCAL(FunctionQueue, staticFunctionQueue, ()); | |
return staticFunctionQueue; | |
} | |
void initializeMainThread() | |
{ | |
mainThreadFunctionQueueMutex(); | |
initializeMainThreadPlatform(); | |
} | |
// 0.1 sec delays in UI is approximate threshold when they become noticeable. Have a limit that's half of that. | |
static const double maxRunLoopSuspensionTime = 0.05; | |
void dispatchFunctionsFromMainThread() | |
{ | |
ASSERT(isMainThread()); | |
if (callbacksPaused) | |
return; | |
double startTime = currentTime(); | |
FunctionWithContext invocation; | |
while (true) { | |
{ | |
MutexLocker locker(mainThreadFunctionQueueMutex()); | |
if (!functionQueue().size()) | |
break; | |
invocation = functionQueue().first(); | |
functionQueue().removeFirst(); | |
} | |
invocation.function(invocation.context); | |
if (invocation.syncFlag) | |
invocation.syncFlag->signal(); | |
// If we are running accumulated functions for too long so UI may become unresponsive, we need to | |
// yield so the user input can be processed. Otherwise user may not be able to even close the window. | |
// This code has effect only in case the scheduleDispatchFunctionsOnMainThread() is implemented in a way that | |
// allows input events to be processed before we are back here. | |
if (currentTime() - startTime > maxRunLoopSuspensionTime) { | |
scheduleDispatchFunctionsOnMainThread(); | |
break; | |
} | |
} | |
} | |
void callOnMainThread(MainThreadFunction* function, void* context) | |
{ | |
ASSERT(function); | |
bool needToSchedule = false; | |
{ | |
MutexLocker locker(mainThreadFunctionQueueMutex()); | |
needToSchedule = functionQueue().size() == 0; | |
functionQueue().append(FunctionWithContext(function, context)); | |
} | |
if (needToSchedule) | |
scheduleDispatchFunctionsOnMainThread(); | |
} | |
void callOnMainThreadAndWait(MainThreadFunction* function, void* context) | |
{ | |
ASSERT(function); | |
if (isMainThread()) { | |
function(context); | |
return; | |
} | |
ThreadCondition syncFlag; | |
Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); | |
MutexLocker locker(functionQueueMutex); | |
functionQueue().append(FunctionWithContext(function, context, &syncFlag)); | |
if (functionQueue().size() == 1) | |
scheduleDispatchFunctionsOnMainThread(); | |
syncFlag.wait(functionQueueMutex); | |
} | |
void setMainThreadCallbacksPaused(bool paused) | |
{ | |
ASSERT(isMainThread()); | |
if (callbacksPaused == paused) | |
return; | |
callbacksPaused = paused; | |
if (!callbacksPaused) | |
scheduleDispatchFunctionsOnMainThread(); | |
} | |
} // namespace WTF |