#include "config.h"
#include "MainThread.h"
+#include "CurrentTime.h"
+#include "Deque.h"
#include "StdLibExtras.h"
#include "Threading.h"
-#include "Vector.h"
+
+#include <wtf/iphone/WebCoreThread.h>
+
+#if PLATFORM(CHROMIUM)
+#error Chromium uses a different main thread implementation
+#endif
namespace WTF {
, 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<FunctionWithContext> FunctionQueue;
+
+typedef Deque<FunctionWithContext> 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;
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());
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)
}
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;
scheduleDispatchFunctionsOnMainThread();
}
+#if !PLATFORM(MAC) && !PLATFORM(QT) && !PLATFORM(BREWMP)
+bool isMainThread()
+{
+ return currentThread() == mainThreadIdentifier;
+}
+#endif
+
} // namespace WTF