Index: Source/core/workers/WorkerThread.cpp |
diff --git a/Source/core/workers/WorkerThread.cpp b/Source/core/workers/WorkerThread.cpp |
index e84fd393fc20ee4a0c4a64870fcef6137b2950a7..ff9c7044721cb6732908f6689768c286d6a73c24 100644 |
--- a/Source/core/workers/WorkerThread.cpp |
+++ b/Source/core/workers/WorkerThread.cpp |
@@ -35,9 +35,12 @@ |
#include "core/workers/WorkerReportingProxy.h" |
#include "core/workers/WorkerThreadStartupData.h" |
#include "platform/PlatformThreadData.h" |
+#include "platform/Task.h" |
+#include "platform/ThreadTimers.h" |
#include "platform/heap/ThreadState.h" |
#include "platform/weborigin/KURL.h" |
#include "public/platform/Platform.h" |
+#include "public/platform/WebThread.h" |
#include "public/platform/WebWaitableEvent.h" |
#include "public/platform/WebWorkerRunLoop.h" |
#include "wtf/Noncopyable.h" |
@@ -47,6 +50,11 @@ |
namespace blink { |
+namespace { |
+const int64 kShortIdleHandlerDelayMs = 1000; |
+const int64 kLongIdleHandlerDelayMs = 10*1000; |
+} |
+ |
static Mutex& threadSetMutex() |
{ |
AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); |
@@ -65,8 +73,97 @@ unsigned WorkerThread::workerThreadCount() |
return workerThreads().size(); |
} |
+class WorkerSharedTimer : public SharedTimer { |
+public: |
+ WorkerSharedTimer(blink::WebThread* web_thread) |
jochen (gone - plz use gerrit)
2014/07/24 12:12:33
explicit and webThread
nasko
2014/07/24 12:53:18
Done.
|
+ : m_thread(web_thread) |
+ , m_nextFireTime(0.0) |
+ { } |
+ |
+ virtual void setFiredFunction(blink::WebThread::SharedTimerFunction func) |
+ { |
+ if (m_thread) |
jochen (gone - plz use gerrit)
2014/07/24 12:12:33
when can m_thread be null?
nasko
2014/07/24 12:53:18
Shouldn't be null.
|
+ m_thread->setSharedTimerFiredFunction(func); |
+ else |
+ m_nextFireTime = 0.0; |
+ } |
+ virtual void setFireInterval(double interval) |
+ { |
+ if (m_thread) |
+ m_thread->setSharedTimerFireInterval(interval); |
+ m_nextFireTime = interval + currentTime(); |
+ } |
+ virtual void stop() |
+ { |
+ if (m_thread) |
+ m_thread->stopSharedTimer(); |
+ } |
+ |
+ double nextFireTime() { return m_nextFireTime; } |
+ |
+private: |
+ blink::WebThread* m_thread; |
+ double m_nextFireTime; |
+}; |
+ |
+class WorkerThreadTask : public blink::WebThread::Task { |
+ WTF_MAKE_NONCOPYABLE(WorkerThreadTask); WTF_MAKE_FAST_ALLOCATED; |
+public: |
+ static PassOwnPtr<WorkerThreadTask> create(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented) |
+ { |
+ return adoptPtr(new WorkerThreadTask(workerThread, task, isInstrumented)); |
+ } |
+ |
+ virtual ~WorkerThreadTask() { } |
+ |
+ virtual void run() OVERRIDE |
+ { |
+ WorkerGlobalScope* workerGlobalScope = m_workerThread.workerGlobalScope(); |
+ if (m_isInstrumented) |
+ InspectorInstrumentation::willPerformExecutionContextTask(workerGlobalScope, m_task.get()); |
+ if ((!workerGlobalScope->isClosing() && !m_workerThread.terminated()) || m_task->isCleanupTask()) |
+ m_task->performTask(workerGlobalScope); |
+ if (m_isInstrumented) |
+ InspectorInstrumentation::didPerformExecutionContextTask(workerGlobalScope); |
+ } |
+ |
+private: |
+ WorkerThreadTask(const WorkerThread& workerThread, PassOwnPtr<ExecutionContextTask> task, bool isInstrumented) |
+ : m_workerThread(workerThread) |
+ , m_task(task) |
+ , m_isInstrumented(isInstrumented) |
+ { |
+ if (m_isInstrumented) |
+ m_isInstrumented = !m_task->taskNameForInstrumentation().isEmpty(); |
+ if (m_isInstrumented) |
+ InspectorInstrumentation::didPostExecutionContextTask(m_workerThread.workerGlobalScope(), m_task.get()); |
+ } |
+ |
+ const WorkerThread& m_workerThread; |
+ OwnPtr<ExecutionContextTask> m_task; |
+ bool m_isInstrumented; |
+}; |
+ |
+class RunDebuggerQueueTask FINAL : public ExecutionContextTask { |
+public: |
+ static PassOwnPtr<RunDebuggerQueueTask> create(WorkerThread* thread) |
+ { |
+ return adoptPtr(new RunDebuggerQueueTask(thread)); |
+ } |
+ virtual void performTask(ExecutionContext* context) OVERRIDE |
+ { |
+ ASSERT(context->isWorkerGlobalScope()); |
+ m_thread->runDebuggerTask(WorkerThread::DontWaitForMessage); |
+ } |
+ |
+private: |
+ explicit RunDebuggerQueueTask(WorkerThread* thread) : m_thread(thread) { } |
+ |
+ WorkerThread* m_thread; |
+}; |
+ |
WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData) |
- : m_threadID(0) |
+ : m_terminated(false) |
, m_workerLoaderProxy(workerLoaderProxy) |
, m_workerReportingProxy(workerReportingProxy) |
, m_startupData(startupData) |
@@ -83,25 +180,17 @@ WorkerThread::~WorkerThread() |
workerThreads().remove(this); |
} |
-bool WorkerThread::start() |
+void WorkerThread::start() |
{ |
- // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts. |
- MutexLocker lock(m_threadCreationMutex); |
- |
- if (m_threadID) |
- return true; |
- |
- m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker"); |
- |
- return m_threadID; |
-} |
+ if (m_thread) |
+ return; |
-void WorkerThread::workerThreadStart(void* thread) |
-{ |
- static_cast<WorkerThread*>(thread)->workerThread(); |
+ m_thread = adoptPtr(blink::Platform::current()->createThread("WebCore: Worker")); |
+ m_sharedTimer = adoptPtr(new WorkerSharedTimer(m_thread.get())); |
+ m_thread->postTask(new Task(WTF::bind(&WorkerThread::initialize, this))); |
} |
-void WorkerThread::workerThread() |
+void WorkerThread::initialize() |
{ |
KURL scriptURL = m_startupData->m_scriptURL; |
String sourceCode = m_startupData->m_sourceCode; |
@@ -109,19 +198,22 @@ void WorkerThread::workerThread() |
{ |
MutexLocker lock(m_threadCreationMutex); |
+ |
ThreadState::attach(); |
m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release()); |
- m_runLoop.setWorkerGlobalScope(workerGlobalScope()); |
- if (m_runLoop.terminated()) { |
+ PlatformThreadData::current().threadTimers().setSharedTimer(m_sharedTimer.get()); |
+ |
+ if (m_terminated) { |
// The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, |
// forbidExecution() couldn't be called from stop(). |
m_workerGlobalScope->script()->forbidExecution(); |
} |
} |
- // The corresponding call to didStopWorkerRunLoop is in |
+ |
+ // The corresponding call to didStopWorkerThread is in |
// ~WorkerScriptController. |
- blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(&m_runLoop)); |
+ blink::Platform::current()->didStartWorkerThread(m_thread.get()); |
// Notify proxy that a new WorkerGlobalScope has been created and started. |
m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get()); |
@@ -132,13 +224,19 @@ void WorkerThread::workerThread() |
InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode); |
script->evaluate(ScriptSourceCode(sourceCode, scriptURL)); |
- runEventLoop(); |
+ postInitialize(); |
+ |
+ m_weakFactory = adoptPtr(new WeakPtrFactory<WorkerThread>(this)); |
+ m_thread->postDelayedTask(new Task(WTF::bind(&WorkerThread::idleHandler, m_weakFactory->createWeakPtr())), kShortIdleHandlerDelayMs); |
+} |
+ |
+void WorkerThread::cleanup() |
+{ |
+ m_weakFactory.release(); |
// This should be called before we start the shutdown procedure. |
workerReportingProxy().willDestroyWorkerGlobalScope(); |
- ThreadIdentifier threadID = m_threadID; |
- |
// The below assignment will destroy the context, which will in turn notify messaging proxy. |
// We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. |
// If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out. |
@@ -165,14 +263,10 @@ void WorkerThread::workerThread() |
// Clean up PlatformThreadData before WTF::WTFThreadData goes away! |
PlatformThreadData::current().destroy(); |
- // The thread object may be already destroyed from notification now, don't try to access "this". |
- detachThread(threadID); |
-} |
- |
-void WorkerThread::runEventLoop() |
-{ |
- // Does not return until terminated. |
- m_runLoop.run(); |
+ // Set the timer function to null, which will cause the shared timer in |
+ // WebThread to be deleted. It is important to do it here, since it |
+ // should be deleted on the same thread that it executes on. |
+ m_sharedTimer->setFiredFunction(0); |
} |
class WorkerThreadShutdownFinishTask : public ExecutionContextTask { |
@@ -188,6 +282,7 @@ public: |
workerGlobalScope->clearInspector(); |
// It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. |
workerGlobalScope->clearScript(); |
+ workerGlobalScope->thread()->webThread()->postTask(new Task(WTF::bind(&WorkerThread::cleanup, workerGlobalScope->thread()))); |
} |
virtual bool isCleanupTask() const { return true; } |
@@ -223,9 +318,13 @@ void WorkerThread::stop() |
// Prevent the deadlock between GC and an attempt to stop a thread. |
ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack); |
- // Mutex protection is necessary because stop() can be called before the context is fully created. |
+ // Protect against this method and initialize() racing each other. |
MutexLocker lock(m_threadCreationMutex); |
+ // If stop has already been called, just return. |
+ if (m_terminated) |
+ return; |
+ |
// Signal the thread to notify that the thread's stopping. |
if (m_shutdownEvent) |
m_shutdownEvent->signal(); |
@@ -234,30 +333,77 @@ void WorkerThread::stop() |
if (m_workerGlobalScope) { |
m_workerGlobalScope->script()->scheduleExecutionTermination(); |
m_workerGlobalScope->wasRequestedToTerminate(); |
- m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create()); |
+ InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get()); |
+ postTask(WorkerThreadShutdownStartTask::create()); |
+ m_terminated = true; |
return; |
} |
- m_runLoop.terminate(); |
} |
bool WorkerThread::isCurrentThread() const |
{ |
- return m_threadID == currentThread(); |
+ return m_thread && m_thread->isCurrentThread(); |
+} |
+ |
+void WorkerThread::idleHandler() |
+{ |
+ if (m_terminated) |
+ return; |
+ |
+ int64 delay = kLongIdleHandlerDelayMs; |
+ |
+ // Do a script engine idle notification if the next event is distant enough. |
+ const double kMinIdleTimespan = 0.3; |
+ if (!m_sharedTimer->nextFireTime() || m_sharedTimer->nextFireTime() > currentTime() + kMinIdleTimespan) { |
+ bool hasMoreWork = !m_workerGlobalScope->idleNotification(); |
+ if (hasMoreWork) |
+ delay = kShortIdleHandlerDelayMs; |
+ } |
+ |
+ m_thread->postDelayedTask(new Task(WTF::bind(&WorkerThread::idleHandler, m_weakFactory->createWeakPtr())), delay); |
} |
void WorkerThread::postTask(PassOwnPtr<ExecutionContextTask> task) |
{ |
- m_runLoop.postTask(task); |
+ m_thread->postTask(WorkerThreadTask::create(*this, task, true).leakPtr()); |
} |
void WorkerThread::postDebuggerTask(PassOwnPtr<ExecutionContextTask> task) |
{ |
- m_runLoop.postDebuggerTask(task); |
+ m_debuggerMessageQueue.append(WorkerThreadTask::create(*this, task, false)); |
+ postTask(RunDebuggerQueueTask::create(this)); |
+} |
+ |
+MessageQueueWaitResult WorkerThread::runDebuggerTask(WaitMode waitMode) |
+{ |
+ ASSERT(isCurrentThread()); |
+ MessageQueueWaitResult result; |
+ double absoluteTime = MessageQueue<blink::WebThread::Task>::infiniteTime(); |
+ OwnPtr<blink::WebThread::Task> task; |
+ { |
+ if (waitMode == DontWaitForMessage) |
+ absoluteTime = 0.0; |
+ ThreadState::SafePointScope safePointScope(ThreadState::NoHeapPointersOnStack); |
+ task = m_debuggerMessageQueue.waitForMessageWithTimeout(result, absoluteTime); |
+ } |
+ |
+ if (result == MessageQueueMessageReceived) { |
+ InspectorInstrumentation::willProcessTask(workerGlobalScope()); |
+ task->run(); |
+ InspectorInstrumentation::didProcessTask(workerGlobalScope()); |
+ } |
+ |
+ return result; |
+} |
+ |
+void WorkerThread::willEnterNestedLoop() |
+{ |
+ InspectorInstrumentation::willEnterNestedRunLoop(m_workerGlobalScope.get()); |
} |
-MessageQueueWaitResult WorkerThread::runDebuggerTask(WorkerRunLoop::WaitMode waitMode) |
+void WorkerThread::didLeaveNestedLoop() |
{ |
- return m_runLoop.runDebuggerTask(waitMode); |
+ InspectorInstrumentation::didLeaveNestedRunLoop(m_workerGlobalScope.get()); |
} |
} // namespace blink |