1 /* 2 * Copyright (C) 2008 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 * 25 */ 26 27 #include "config.h" 28 29 #include "core/workers/WorkerThread.h" 30 31 #include "bindings/v8/ScriptSourceCode.h" 32 #include "core/inspector/InspectorInstrumentation.h" 33 #include "core/workers/DedicatedWorkerGlobalScope.h" 34 #include "core/workers/WorkerClients.h" 35 #include "core/workers/WorkerReportingProxy.h" 36 #include "core/workers/WorkerThreadStartupData.h" 37 #include "platform/PlatformThreadData.h" 38 #include "platform/heap/ThreadState.h" 39 #include "platform/weborigin/KURL.h" 40 #include "public/platform/Platform.h" 41 #include "public/platform/WebWaitableEvent.h" 42 #include "public/platform/WebWorkerRunLoop.h" 43 #include "wtf/Noncopyable.h" 44 #include "wtf/text/WTFString.h" 45 46 #include <utility> 47 48 namespace WebCore { 49 50 static Mutex& threadSetMutex() 51 { 52 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); 53 return mutex; 54 } 55 56 static HashSet<WorkerThread*>& workerThreads() 57 { 58 DEFINE_STATIC_LOCAL(HashSet<WorkerThread*>, threads, ()); 59 return threads; 60 } 61 62 unsigned WorkerThread::workerThreadCount() 63 { 64 MutexLocker lock(threadSetMutex()); 65 return workerThreads().size(); 66 } 67 68 WorkerThread::WorkerThread(WorkerLoaderProxy& workerLoaderProxy, WorkerReportingProxy& workerReportingProxy, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData) 69 : m_threadID(0) 70 , m_workerLoaderProxy(workerLoaderProxy) 71 , m_workerReportingProxy(workerReportingProxy) 72 , m_startupData(startupData) 73 , m_notificationClient(0) 74 , m_shutdownEvent(adoptPtr(blink::Platform::current()->createWaitableEvent())) 75 { 76 MutexLocker lock(threadSetMutex()); 77 workerThreads().add(this); 78 } 79 80 WorkerThread::~WorkerThread() 81 { 82 MutexLocker lock(threadSetMutex()); 83 ASSERT(workerThreads().contains(this)); 84 workerThreads().remove(this); 85 } 86 87 bool WorkerThread::start() 88 { 89 // Mutex protection is necessary to ensure that m_threadID is initialized when the thread starts. 90 MutexLocker lock(m_threadCreationMutex); 91 92 if (m_threadID) 93 return true; 94 95 m_threadID = createThread(WorkerThread::workerThreadStart, this, "WebCore: Worker"); 96 97 return m_threadID; 98 } 99 100 void WorkerThread::workerThreadStart(void* thread) 101 { 102 static_cast<WorkerThread*>(thread)->workerThread(); 103 } 104 105 void WorkerThread::workerThread() 106 { 107 KURL scriptURL = m_startupData->m_scriptURL; 108 String sourceCode = m_startupData->m_sourceCode; 109 WorkerThreadStartMode startMode = m_startupData->m_startMode; 110 111 { 112 MutexLocker lock(m_threadCreationMutex); 113 ThreadState::attach(); 114 m_workerGlobalScope = createWorkerGlobalScope(m_startupData.release()); 115 m_runLoop.setWorkerGlobalScope(workerGlobalScope()); 116 117 if (m_runLoop.terminated()) { 118 // The worker was terminated before the thread had a chance to run. Since the context didn't exist yet, 119 // forbidExecution() couldn't be called from stop(). 120 m_workerGlobalScope->script()->forbidExecution(); 121 } 122 } 123 // The corresponding call to didStopWorkerRunLoop is in 124 // ~WorkerScriptController. 125 blink::Platform::current()->didStartWorkerRunLoop(blink::WebWorkerRunLoop(&m_runLoop)); 126 127 // Notify proxy that a new WorkerGlobalScope has been created and started. 128 m_workerReportingProxy.workerGlobalScopeStarted(m_workerGlobalScope.get()); 129 130 WorkerScriptController* script = m_workerGlobalScope->script(); 131 if (!script->isExecutionForbidden()) 132 script->initializeContextIfNeeded(); 133 InspectorInstrumentation::willEvaluateWorkerScript(workerGlobalScope(), startMode); 134 script->evaluate(ScriptSourceCode(sourceCode, scriptURL)); 135 136 runEventLoop(); 137 138 // This should be called before we start the shutdown procedure. 139 workerReportingProxy().willDestroyWorkerGlobalScope(); 140 141 ThreadIdentifier threadID = m_threadID; 142 143 // The below assignment will destroy the context, which will in turn notify messaging proxy. 144 // We cannot let any objects survive past thread exit, because no other thread will run GC or otherwise destroy them. 145 // If Oilpan is enabled, we detach of the context/global scope, with the final heap cleanup below sweeping it out. 146 #if !ENABLE(OILPAN) 147 ASSERT(m_workerGlobalScope->hasOneRef()); 148 #endif 149 m_workerGlobalScope->dispose(); 150 m_workerGlobalScope = nullptr; 151 152 // Detach the ThreadState, cleaning out the thread's heap by 153 // performing a final GC. The cleanup operation will at the end 154 // assert that the heap is empty. If the heap does not become 155 // empty, there are still pointers into the heap and those 156 // pointers will be dangling after thread termination because we 157 // are destroying the heap. It is important to detach while the 158 // thread is still valid. In particular, finalizers for objects in 159 // the heap for this thread will need to access thread local data. 160 ThreadState::detach(); 161 162 // Notify the proxy that the WorkerGlobalScope has been disposed of. 163 // This can free this thread object, hence it must not be touched afterwards. 164 workerReportingProxy().workerGlobalScopeDestroyed(); 165 166 // Clean up PlatformThreadData before WTF::WTFThreadData goes away! 167 PlatformThreadData::current().destroy(); 168 169 // The thread object may be already destroyed from notification now, don't try to access "this". 170 detachThread(threadID); 171 } 172 173 void WorkerThread::runEventLoop() 174 { 175 // Does not return until terminated. 176 m_runLoop.run(); 177 } 178 179 class WorkerThreadShutdownFinishTask : public ExecutionContextTask { 180 public: 181 static PassOwnPtr<WorkerThreadShutdownFinishTask> create() 182 { 183 return adoptPtr(new WorkerThreadShutdownFinishTask()); 184 } 185 186 virtual void performTask(ExecutionContext *context) 187 { 188 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 189 workerGlobalScope->clearInspector(); 190 // It's not safe to call clearScript until all the cleanup tasks posted by functions initiated by WorkerThreadShutdownStartTask have completed. 191 workerGlobalScope->clearScript(); 192 } 193 194 virtual bool isCleanupTask() const { return true; } 195 }; 196 197 class WorkerThreadShutdownStartTask : public ExecutionContextTask { 198 public: 199 static PassOwnPtr<WorkerThreadShutdownStartTask> create() 200 { 201 return adoptPtr(new WorkerThreadShutdownStartTask()); 202 } 203 204 virtual void performTask(ExecutionContext *context) 205 { 206 WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context); 207 workerGlobalScope->stopFetch(); 208 workerGlobalScope->stopActiveDOMObjects(); 209 210 // Event listeners would keep DOMWrapperWorld objects alive for too long. Also, they have references to JS objects, 211 // which become dangling once Heap is destroyed. 212 workerGlobalScope->removeAllEventListeners(); 213 214 // Stick a shutdown command at the end of the queue, so that we deal 215 // with all the cleanup tasks the databases post first. 216 workerGlobalScope->postTask(WorkerThreadShutdownFinishTask::create()); 217 } 218 219 virtual bool isCleanupTask() const { return true; } 220 }; 221 222 void WorkerThread::stop() 223 { 224 // Prevent the deadlock between GC and an attempt to stop a thread. 225 ThreadState::SafePointScope safePointScope(ThreadState::HeapPointersOnStack); 226 227 // Mutex protection is necessary because stop() can be called before the context is fully created. 228 MutexLocker lock(m_threadCreationMutex); 229 230 // Signal the thread to notify that the thread's stopping. 231 if (m_shutdownEvent) 232 m_shutdownEvent->signal(); 233 234 // Ensure that tasks are being handled by thread event loop. If script execution weren't forbidden, a while(1) loop in JS could keep the thread alive forever. 235 if (m_workerGlobalScope) { 236 m_workerGlobalScope->script()->scheduleExecutionTermination(); 237 m_workerGlobalScope->wasRequestedToTerminate(); 238 m_runLoop.postTaskAndTerminate(WorkerThreadShutdownStartTask::create()); 239 return; 240 } 241 m_runLoop.terminate(); 242 } 243 244 bool WorkerThread::isCurrentThread() const 245 { 246 return m_threadID == currentThread(); 247 } 248 249 } // namespace WebCore 250