Chromium Code Reviews| Index: Source/core/workers/WorkerThread.cpp |
| diff --git a/Source/core/workers/WorkerThread.cpp b/Source/core/workers/WorkerThread.cpp |
| index e84fd393fc20ee4a0c4a64870fcef6137b2950a7..100e9ed94021f9d1c5a37d4d0d19d7123eb66769 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,94 @@ unsigned WorkerThread::workerThreadCount() |
| return workerThreads().size(); |
| } |
| +class WorkerSharedTimer : public SharedTimer { |
| +public: |
| + explicit WorkerSharedTimer(blink::WebThread* webThread) |
| + : m_thread(webThread) |
| + , m_nextFireTime(0.0) |
| + { } |
| + |
| + virtual void setFiredFunction(blink::WebThread::SharedTimerFunction func) |
| + { |
| + m_thread->setSharedTimerFiredFunction(func); |
| + if (!func) |
| + m_nextFireTime = 0.0; |
| + } |
| + virtual void setFireInterval(double interval) |
| + { |
| + m_thread->setSharedTimerFireInterval(interval); |
| + m_nextFireTime = interval + currentTime(); |
| + } |
| + virtual void stop() |
| + { |
| + 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 +177,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 +195,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 +221,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 +260,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 +279,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; } |
| @@ -205,6 +297,7 @@ public: |
| WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); |
| workerGlobalScope->stopFetch(); |
| workerGlobalScope->stopActiveDOMObjects(); |
| + workerGlobalScope->thread()->webThread()->stopSharedTimer(); |
| // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, |
| // which become dangling once Heap is destroyed. |
| @@ -223,9 +316,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 +331,77 @@ void WorkerThread::stop() |
| if (m_workerGlobalScope) { |
| m_workerGlobalScope->script()->scheduleExecutionTermination(); |
| m_workerGlobalScope->wasRequestedToTerminate(); |
| - m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create()); |
| + InspectorInstrumentation::didKillAllExecutionContextTasks(m_workerGlobalScope.get()); |
|
aandrey
2014/07/24 16:07:53
is it safe to access m_workerGlobalScope here?
if
nasko
2014/07/25 06:30:14
It is safe. It is used further in the WorkerThread
yurys
2014/07/25 06:41:56
It is unsafe to access m_workerGlobalScope here as
|
| + 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 |