X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..14957cd040308e3eeec43d26bae5d76da13fcd85:/wtf/MainThread.cpp?ds=sidebyside diff --git a/wtf/MainThread.cpp b/wtf/MainThread.cpp index c7a6caa..3229e5b 100644 --- a/wtf/MainThread.cpp +++ b/wtf/MainThread.cpp @@ -29,9 +29,16 @@ #include "config.h" #include "MainThread.h" +#include "CurrentTime.h" +#include "Deque.h" #include "StdLibExtras.h" #include "Threading.h" -#include "Vector.h" + +#include + +#if PLATFORM(CHROMIUM) +#error Chromium uses a different main thread implementation +#endif namespace WTF { @@ -46,13 +53,30 @@ struct FunctionWithContext { , syncFlag(syncFlag) { } + bool operator == (const FunctionWithContext& o) + { + return function == o.function + && context == o.context + && syncFlag == o.syncFlag; + } +}; + +class FunctionWithContextFinder { +public: + FunctionWithContextFinder(const FunctionWithContext& m) : m(m) {} + bool operator() (FunctionWithContext& o) { return o == m; } + FunctionWithContext m; }; -typedef Vector FunctionQueue; + +typedef Deque FunctionQueue; static bool callbacksPaused; // This global variable is only accessed from main thread. +#if !PLATFORM(MAC) && !PLATFORM(QT) +static ThreadIdentifier mainThreadIdentifier; +#endif -Mutex& mainThreadFunctionQueueMutex() +static Mutex& mainThreadFunctionQueueMutex() { DEFINE_STATIC_LOCAL(Mutex, staticMutex, ()); return staticMutex; @@ -64,13 +88,54 @@ static FunctionQueue& functionQueue() return staticFunctionQueue; } -#if !PLATFORM(WIN) + +#if !PLATFORM(MAC) + void initializeMainThread() +{ + static bool initializedMainThread; + if (initializedMainThread) + return; + initializedMainThread = true; + +#if !PLATFORM(QT) + mainThreadIdentifier = currentThread(); +#endif + + mainThreadFunctionQueueMutex(); + initializeMainThreadPlatform(); +} + +#else + +static pthread_once_t initializeMainThreadKeyOnce = PTHREAD_ONCE_INIT; + +static void initializeMainThreadOnce() { mainThreadFunctionQueueMutex(); + initializeMainThreadPlatform(); +} + +void initializeMainThread() +{ + pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadOnce); +} + +static void initializeMainThreadToProcessMainThreadOnce() +{ + mainThreadFunctionQueueMutex(); + initializeMainThreadToProcessMainThreadPlatform(); +} + +void initializeMainThreadToProcessMainThread() +{ + pthread_once(&initializeMainThreadKeyOnce, initializeMainThreadToProcessMainThreadOnce); } #endif +// 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()); @@ -78,30 +143,45 @@ void dispatchFunctionsFromMainThread() if (callbacksPaused) return; - FunctionQueue queueCopy; - { - MutexLocker locker(mainThreadFunctionQueueMutex()); - queueCopy.swap(functionQueue()); - } + double startTime = currentTime(); + + FunctionWithContext invocation; + while (true) { + { + MutexLocker locker(mainThreadFunctionQueueMutex()); + if (!functionQueue().size()) + break; + invocation = functionQueue().takeFirst(); + } - for (unsigned i = 0; i < queueCopy.size(); ++i) { - FunctionWithContext& invocation = queueCopy[i]; invocation.function(invocation.context); - if (invocation.syncFlag) + if (invocation.syncFlag) { + MutexLocker locker(mainThreadFunctionQueueMutex()); 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)); } - - scheduleDispatchFunctionsOnMainThread(); + if (needToSchedule) + scheduleDispatchFunctionsOnMainThread(); } void callOnMainThreadAndWait(MainThreadFunction* function, void* context) @@ -114,21 +194,35 @@ void callOnMainThreadAndWait(MainThreadFunction* function, void* context) } ThreadCondition syncFlag; - Mutex conditionMutex; + Mutex& functionQueueMutex = mainThreadFunctionQueueMutex(); + MutexLocker locker(functionQueueMutex); + functionQueue().append(FunctionWithContext(function, context, &syncFlag)); + if (functionQueue().size() == 1) + scheduleDispatchFunctionsOnMainThread(); + syncFlag.wait(functionQueueMutex); +} - { - MutexLocker locker(mainThreadFunctionQueueMutex()); - functionQueue().append(FunctionWithContext(function, context, &syncFlag)); - conditionMutex.lock(); - } +void cancelCallOnMainThread(MainThreadFunction* function, void* context) +{ + ASSERT(function); + + MutexLocker locker(mainThreadFunctionQueueMutex()); + + FunctionWithContextFinder pred(FunctionWithContext(function, context)); - scheduleDispatchFunctionsOnMainThread(); - syncFlag.wait(conditionMutex); + while (true) { + // We must redefine 'i' each pass, because the itererator's operator= + // requires 'this' to be valid, and remove() invalidates all iterators + FunctionQueue::iterator i(functionQueue().findIf(pred)); + if (i == functionQueue().end()) + break; + functionQueue().remove(i); + } } void setMainThreadCallbacksPaused(bool paused) { - ASSERT(isMainThread()); + ASSERT((isMainThread() || pthread_main_np()) && WebCoreWebThreadIsLockedOrDisabled()); if (callbacksPaused == paused) return; @@ -139,4 +233,11 @@ void setMainThreadCallbacksPaused(bool paused) scheduleDispatchFunctionsOnMainThread(); } +#if !PLATFORM(MAC) && !PLATFORM(QT) && !PLATFORM(BREWMP) +bool isMainThread() +{ + return currentThread() == mainThreadIdentifier; +} +#endif + } // namespace WTF